summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLulu Cathrinus Grimalkin <me@erkin.party>2021-11-24 14:27:08 +0300
committerGitHub <noreply@github.com>2021-11-24 14:27:08 +0300
commitc0eff50f7be4ee365d0b5ce828f64a66574c4f06 (patch)
treebcffc2c52f29d12ef20854fbea2d2e60b7f7627e
parentc0b09fe341c7ddced42479e0192a28ca553e30d6 (diff)
parent771301fea060467945e6c55379dd8e761aa9ad9d (diff)
downloadvyos-1x-c0eff50f7be4ee365d0b5ce828f64a66574c4f06.tar.gz
vyos-1x-c0eff50f7be4ee365d0b5ce828f64a66574c4f06.zip
Merge branch 'vyos:equuleus' into equuleus
-rw-r--r--Makefile20
-rw-r--r--data/templates/accel-ppp/l2tp.config.tmpl3
-rw-r--r--data/templates/accel-ppp/pptp.config.tmpl7
-rw-r--r--data/templates/accel-ppp/sstp.config.tmpl8
-rw-r--r--data/templates/conserver/dropbear@.service.tmpl2
-rw-r--r--data/templates/dhcp-client/ipv4.tmpl3
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl99
-rw-r--r--data/templates/dns-forwarding/recursor.conf.tmpl12
-rw-r--r--data/templates/frr/isisd.frr.tmpl72
-rw-r--r--data/templates/frr/route-map.frr.tmpl5
-rw-r--r--data/templates/https/nginx.default.tmpl5
-rw-r--r--data/templates/lcd/LCDd.conf.tmpl7
-rw-r--r--data/templates/ntp/ntpd.conf.tmpl2
-rw-r--r--data/templates/openvpn/server.conf.tmpl34
-rw-r--r--data/templates/openvpn/service-override.conf.tmpl20
-rw-r--r--data/templates/pppoe/peer.tmpl19
-rw-r--r--data/templates/syslog/rsyslog.conf.tmpl58
-rw-r--r--data/templates/vrrp/daemon.tmpl5
-rw-r--r--data/templates/vrrp/keepalived.conf.tmpl161
-rw-r--r--debian/control2
-rw-r--r--debian/vyos-1x.install1
-rw-r--r--interface-definitions/bcast-relay.xml.in5
-rw-r--r--interface-definitions/dhcp-relay.xml.in4
-rw-r--r--interface-definitions/dhcp-server.xml.in193
-rw-r--r--interface-definitions/dhcpv6-relay.xml.in2
-rw-r--r--interface-definitions/dhcpv6-server.xml.in60
-rw-r--r--interface-definitions/dns-domain-name.xml.in25
-rw-r--r--interface-definitions/dns-dynamic.xml.in2
-rw-r--r--interface-definitions/dns-forwarding.xml.in31
-rw-r--r--interface-definitions/flow-accounting-conf.xml.in16
-rw-r--r--interface-definitions/https.xml.in10
-rw-r--r--interface-definitions/igmp-proxy.xml.in2
-rw-r--r--interface-definitions/include/accel-ppp/auth-local-users.xml.i3
-rw-r--r--interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i1
-rw-r--r--interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i2
-rw-r--r--interface-definitions/include/accel-ppp/ppp-options-ipv4.xml.i23
-rw-r--r--interface-definitions/include/accel-ppp/ppp-options-ipv6.xml.i31
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions.xml.i12
-rw-r--r--interface-definitions/include/bfd.xml.i8
-rw-r--r--interface-definitions/include/dhcp/domain-name.xml.i11
-rw-r--r--interface-definitions/include/dhcp/domain-search.xml.i (renamed from interface-definitions/include/dhcp-server-domain-search.xml.i)2
-rw-r--r--interface-definitions/include/dhcp/ntp-server.xml.i15
-rw-r--r--interface-definitions/include/dhcp/ping-check.xml.i8
-rw-r--r--interface-definitions/include/generic-description.xml.i11
-rw-r--r--interface-definitions/include/interface/arp-cache-timeout.xml.i (renamed from interface-definitions/include/interface/interface-arp-cache-timeout.xml.i)2
-rw-r--r--interface-definitions/include/interface/description.xml.i (renamed from interface-definitions/include/interface/interface-description.xml.i)0
-rw-r--r--interface-definitions/include/interface/dhcpv6-options.xml.i4
-rw-r--r--interface-definitions/include/interface/dial-on-demand.xml.i (renamed from interface-definitions/include/interface/interface-dial-on-demand.xml.i)0
-rw-r--r--interface-definitions/include/interface/disable-arp-filter.xml.i (renamed from interface-definitions/include/interface/interface-disable-arp-filter.xml.i)0
-rw-r--r--interface-definitions/include/interface/disable-forwarding.xml.i (renamed from interface-definitions/include/interface/interface-disable-forwarding.xml.i)2
-rw-r--r--interface-definitions/include/interface/disable-link-detect.xml.i (renamed from interface-definitions/include/interface/interface-disable-link-detect.xml.i)0
-rw-r--r--interface-definitions/include/interface/disable.xml.i (renamed from interface-definitions/include/interface/interface-disable.xml.i)0
-rw-r--r--interface-definitions/include/interface/eapol.xml.i (renamed from interface-definitions/include/interface/interface-eapol.xml.i)0
-rw-r--r--interface-definitions/include/interface/enable-arp-accept.xml.i (renamed from interface-definitions/include/interface/interface-enable-arp-accept.xml.i)0
-rw-r--r--interface-definitions/include/interface/enable-arp-announce.xml.i (renamed from interface-definitions/include/interface/interface-enable-arp-announce.xml.i)0
-rw-r--r--interface-definitions/include/interface/enable-arp-ignore.xml.i (renamed from interface-definitions/include/interface/interface-enable-arp-ignore.xml.i)0
-rw-r--r--interface-definitions/include/interface/enable-proxy-arp.xml.i (renamed from interface-definitions/include/interface/interface-enable-proxy-arp.xml.i)0
-rw-r--r--interface-definitions/include/interface/hw-id.xml.i (renamed from interface-definitions/include/interface/interface-hw-id.xml.i)0
-rw-r--r--interface-definitions/include/interface/interface-ipv4-options.xml.i18
-rw-r--r--interface-definitions/include/interface/ipv4-options.xml.i18
-rw-r--r--interface-definitions/include/interface/ipv6-disable-forwarding.xml.i8
-rw-r--r--interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i8
-rw-r--r--interface-definitions/include/interface/ipv6-options.xml.i (renamed from interface-definitions/include/interface/interface-ipv6-options.xml.i)2
-rw-r--r--interface-definitions/include/interface/mac.xml.i (renamed from interface-definitions/include/interface/interface-mac.xml.i)0
-rw-r--r--interface-definitions/include/interface/mirror.xml.i (renamed from interface-definitions/include/interface/interface-mirror.xml.i)0
-rw-r--r--interface-definitions/include/interface/mtu-1200-16000.xml.i (renamed from interface-definitions/include/interface/interface-mtu-1200-16000.xml.i)2
-rw-r--r--interface-definitions/include/interface/mtu-1450-16000.xml.i (renamed from interface-definitions/include/interface/interface-mtu-1450-16000.xml.i)2
-rw-r--r--interface-definitions/include/interface/mtu-64-8024.xml.i (renamed from interface-definitions/include/interface/interface-mtu-64-8024.xml.i)2
-rw-r--r--interface-definitions/include/interface/mtu-68-1500.xml.i (renamed from interface-definitions/include/interface/interface-mtu-68-1500.xml.i)2
-rw-r--r--interface-definitions/include/interface/mtu-68-16000.xml.i (renamed from interface-definitions/include/interface/interface-mtu-68-16000.xml.i)2
-rw-r--r--interface-definitions/include/interface/parameters-flowlabel.xml.i (renamed from interface-definitions/include/interface/interface-parameters-flowlabel.xml.i)11
-rw-r--r--interface-definitions/include/interface/parameters-key.xml.i (renamed from interface-definitions/include/interface/interface-parameters-key.xml.i)0
-rw-r--r--interface-definitions/include/interface/parameters-tos.xml.i (renamed from interface-definitions/include/interface/interface-parameters-tos.xml.i)2
-rw-r--r--interface-definitions/include/interface/parameters-ttl.xml.i (renamed from interface-definitions/include/interface/interface-parameters-ttl.xml.i)6
-rw-r--r--interface-definitions/include/interface/proxy-arp-pvlan.xml.i (renamed from interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i)0
-rw-r--r--interface-definitions/include/interface/source-validation.xml.i (renamed from interface-definitions/include/interface/interface-source-validation.xml.i)0
-rw-r--r--interface-definitions/include/interface/vif-s.xml.i36
-rw-r--r--interface-definitions/include/interface/vif.xml.i18
-rw-r--r--interface-definitions/include/interface/vrf.xml.i (renamed from interface-definitions/include/interface/interface-vrf.xml.i)0
-rw-r--r--interface-definitions/include/isis-redistribute-ipv4.xml.i56
-rw-r--r--interface-definitions/include/isis/default-information-level.xml.i32
-rw-r--r--interface-definitions/include/isis/metric.xml.i14
-rw-r--r--interface-definitions/include/isis/passive.xml.i8
-rw-r--r--interface-definitions/include/isis/protocol-common-config.xml.i769
-rw-r--r--interface-definitions/include/isis/redistribute-level-1-2.xml.i20
-rw-r--r--interface-definitions/include/name-server-ipv4-ipv6.xml.i (renamed from interface-definitions/include/accel-ppp/name-server.xml.i)4
-rw-r--r--interface-definitions/include/name-server-ipv4.xml.i15
-rw-r--r--interface-definitions/include/name-server-ipv6.xml.i15
-rw-r--r--interface-definitions/include/nat-rule.xml.i2
-rw-r--r--interface-definitions/include/pppoe-access-concentrator.xml.i11
-rw-r--r--interface-definitions/include/route-map.xml.i18
-rw-r--r--interface-definitions/include/source-address-ipv4.xml.i2
-rw-r--r--interface-definitions/interfaces-bonding.xml.in32
-rw-r--r--interface-definitions/interfaces-bridge.xml.in51
-rw-r--r--interface-definitions/interfaces-dummy.xml.in8
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in28
-rw-r--r--interface-definitions/interfaces-geneve.xml.in12
-rw-r--r--interface-definitions/interfaces-l2tpv3.xml.in24
-rw-r--r--interface-definitions/interfaces-loopback.xml.in4
-rw-r--r--interface-definitions/interfaces-macsec.xml.in18
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in56
-rw-r--r--interface-definitions/interfaces-pppoe.xml.in41
-rw-r--r--interface-definitions/interfaces-pseudo-ethernet.xml.in16
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in45
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in18
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in14
-rw-r--r--interface-definitions/interfaces-wireless.xml.in18
-rw-r--r--interface-definitions/interfaces-wwan.xml.in16
-rw-r--r--interface-definitions/ipsec-settings.xml.in2
-rw-r--r--interface-definitions/lldp.xml.in2
-rw-r--r--interface-definitions/ntp.xml.in2
-rw-r--r--interface-definitions/protocols-igmp.xml.in15
-rw-r--r--interface-definitions/protocols-isis.xml.in772
-rw-r--r--interface-definitions/protocols-multicast.xml.in4
-rw-r--r--interface-definitions/protocols-pim.xml.in6
-rw-r--r--interface-definitions/service_console-server.xml.in2
-rw-r--r--interface-definitions/service_ipoe-server.xml.in2
-rw-r--r--interface-definitions/service_pppoe-server.xml.in16
-rw-r--r--interface-definitions/service_router-advert.xml.in50
-rw-r--r--interface-definitions/service_webproxy.xml.in2
-rw-r--r--interface-definitions/snmp.xml.in2
-rw-r--r--interface-definitions/ssh.xml.in4
-rw-r--r--interface-definitions/system-console.xml.in1
-rw-r--r--interface-definitions/system-lcd.xml.in8
-rw-r--r--interface-definitions/system-login.xml.in9
-rw-r--r--interface-definitions/vpn_ipsec.xml.in1167
-rw-r--r--interface-definitions/vpn_l2tp.xml.in11
-rw-r--r--interface-definitions/vpn_openconnect.xml.in4
-rw-r--r--interface-definitions/vpn_pptp.xml.in14
-rw-r--r--interface-definitions/vpn_sstp.xml.in6
-rw-r--r--interface-definitions/vrf.xml.in10
-rw-r--r--interface-definitions/vrrp.xml.in17
-rw-r--r--op-mode-definitions/dhcp.xml.in20
-rw-r--r--op-mode-definitions/dns-forwarding.xml.in3
-rw-r--r--op-mode-definitions/generate-ipsec-debug-archive.xml.in20
-rw-r--r--op-mode-definitions/include/bgp/afi-common.xml.i24
-rw-r--r--op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i14
-rw-r--r--op-mode-definitions/include/bgp/exact-match.xml.i8
-rw-r--r--op-mode-definitions/include/bgp/show-bgp-common.xml.i2
-rw-r--r--op-mode-definitions/ipv4-route.xml.in6
-rw-r--r--op-mode-definitions/ipv6-route.xml.in6
-rw-r--r--op-mode-definitions/monitor-protocol.xml.in10
-rw-r--r--op-mode-definitions/openvpn.xml.in6
-rw-r--r--op-mode-definitions/pppoe-server.xml.in3
-rw-r--r--op-mode-definitions/reset-conntrack.xml.in3
-rw-r--r--op-mode-definitions/reset-vpn.xml.in3
-rw-r--r--op-mode-definitions/restart-frr.xml.in110
-rw-r--r--op-mode-definitions/show-interfaces-bonding.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-bridge.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-dummy.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-ethernet.xml.in14
-rw-r--r--op-mode-definitions/show-interfaces-input.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-l2tpv3.xml.in2
-rw-r--r--op-mode-definitions/show-interfaces-loopback.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-pppoe.xml.in2
-rw-r--r--op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-tunnel.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-vti.xml.in4
-rw-r--r--op-mode-definitions/show-interfaces-vxlan.xml.in2
-rw-r--r--op-mode-definitions/show-interfaces-wireguard.xml.in66
-rw-r--r--op-mode-definitions/show-interfaces-wireless.xml.in82
-rw-r--r--op-mode-definitions/show-interfaces-wwan.xml.in6
-rw-r--r--op-mode-definitions/show-ip-route.xml.in7
-rw-r--r--op-mode-definitions/show-log.xml.in99
-rw-r--r--op-mode-definitions/show-system.xml.in6
-rw-r--r--op-mode-definitions/terminal.xml.in8
-rw-r--r--op-mode-definitions/wireguard.xml.in53
-rw-r--r--op-mode-definitions/wireless.xml.in79
-rw-r--r--python/vyos/airbag.py8
-rw-r--r--python/vyos/configdict.py38
-rw-r--r--python/vyos/configsource.py2
-rw-r--r--python/vyos/configverify.py61
-rw-r--r--python/vyos/defaults.py8
-rw-r--r--python/vyos/ethtool.py200
-rw-r--r--python/vyos/frr.py54
-rw-r--r--python/vyos/ifconfig/bridge.py131
-rw-r--r--python/vyos/ifconfig/control.py19
-rw-r--r--python/vyos/ifconfig/ethernet.py217
-rw-r--r--python/vyos/ifconfig/interface.py229
-rw-r--r--python/vyos/ifconfig/section.py12
-rw-r--r--python/vyos/ifconfig/tunnel.py26
-rw-r--r--python/vyos/ifconfig/vrrp.py9
-rw-r--r--python/vyos/ifconfig/wireguard.py27
-rw-r--r--python/vyos/ifconfig/wwan.py17
-rw-r--r--python/vyos/migrator.py18
-rw-r--r--python/vyos/systemversions.py28
-rw-r--r--python/vyos/template.py15
-rw-r--r--python/vyos/util.py44
-rw-r--r--schema/interface_definition.rnc17
-rw-r--r--schema/interface_definition.rng18
-rwxr-xr-xscripts/build-command-op-templates96
-rwxr-xr-xscripts/build-command-templates2
-rwxr-xr-xscripts/build-component-versions47
-rw-r--r--smoketest/configs/bgp-bfd-communities533
-rw-r--r--smoketest/configs/bgp-big-as-cloud1956
-rw-r--r--smoketest/configs/bgp-dmvpn-hub174
-rw-r--r--smoketest/configs/bgp-dmvpn-spoke201
-rw-r--r--smoketest/configs/dialup-router-complex1662
-rw-r--r--smoketest/configs/dialup-router-medium-vpn1
-rw-r--r--smoketest/configs/isis-small104
-rw-r--r--smoketest/configs/tunnel-broker135
-rw-r--r--smoketest/configs/vrf-basic231
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py46
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py21
-rwxr-xr-xsmoketest/scripts/cli/test_ha_vrrp.py170
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py53
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_geneve.py1
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py1
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pppoe.py1
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py20
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py1
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireguard.py1
-rw-r--r--[-rwxr-xr-x]smoketest/scripts/cli/test_nat.py1
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py1
-rwxr-xr-xsmoketest/scripts/cli/test_policy_local-route.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_igmp-proxy.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py169
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rip.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ripng.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rpki.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_bcast-relay.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-relay.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py123
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-relay.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-server.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_forwarding.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_mdns-repeater.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_router-advert.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_snmp.py34
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_tftp-server.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_webproxy.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_acceleration_qat.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_conntrack.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py80
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_lcd.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py42
-rwxr-xr-xsmoketest/scripts/cli/test_system_nameserver.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py1
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py1
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py1
-rwxr-xr-xsrc/conf_mode/dhcp_server.py55
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py2
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py15
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py2
-rwxr-xr-xsrc/conf_mode/host_name.py49
-rwxr-xr-xsrc/conf_mode/http-api.py12
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py91
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py50
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py47
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py37
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py4
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py39
-rwxr-xr-xsrc/conf_mode/protocols_isis.py258
-rwxr-xr-xsrc/conf_mode/protocols_rip.py3
-rwxr-xr-xsrc/conf_mode/snmp.py19
-rwxr-xr-xsrc/conf_mode/system-login-banner.py15
-rwxr-xr-xsrc/conf_mode/system-login.py6
-rwxr-xr-xsrc/conf_mode/system_console.py70
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py67
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py2
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py6
-rwxr-xr-xsrc/conf_mode/vrrp.py348
-rw-r--r--src/etc/cron.d/check-wwan1
-rw-r--r--src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient25
-rw-r--r--src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup2
-rw-r--r--src/etc/systemd/system/keepalived.service.d/override.conf10
-rw-r--r--src/etc/systemd/system/openvpn@.service.d/10-override.conf14
-rw-r--r--src/etc/systemd/system/openvpn@.service.d/override.conf9
-rw-r--r--src/etc/udev/rules.d/90-vyos-serial.rules4
-rwxr-xr-xsrc/helpers/vyos-check-wwan.py37
-rwxr-xr-xsrc/migration-scripts/dhcp-server/5-to-687
-rwxr-xr-xsrc/migration-scripts/dns-forwarding/1-to-210
-rwxr-xr-xsrc/migration-scripts/interfaces/20-to-21120
-rwxr-xr-xsrc/migration-scripts/interfaces/21-to-2246
-rwxr-xr-xsrc/migration-scripts/isis/0-to-159
-rwxr-xr-xsrc/migration-scripts/system/20-to-2148
-rwxr-xr-xsrc/op_mode/connect_disconnect.py68
-rwxr-xr-xsrc/op_mode/generate_ipsec_debug_archive.sh36
-rwxr-xr-xsrc/op_mode/ping.py10
-rwxr-xr-xsrc/op_mode/powerctrl.py33
-rwxr-xr-xsrc/op_mode/restart_frr.py133
-rwxr-xr-xsrc/op_mode/show_dhcp.py8
-rwxr-xr-xsrc/op_mode/show_interfaces.py34
-rwxr-xr-xsrc/op_mode/show_ipsec_sa.py168
-rwxr-xr-xsrc/op_mode/show_system_integrity.py70
-rwxr-xr-xsrc/op_mode/show_version.py4
-rwxr-xr-xsrc/op_mode/show_wwan.py18
-rw-r--r--src/services/api/graphql/README.graphql140
-rw-r--r--src/services/api/graphql/bindings.py14
-rw-r--r--src/services/api/graphql/graphql/__init__.py0
-rw-r--r--src/services/api/graphql/graphql/directives.py32
-rw-r--r--src/services/api/graphql/graphql/mutations.py109
-rw-r--r--src/services/api/graphql/graphql/schema/config_file.graphql27
-rw-r--r--src/services/api/graphql/graphql/schema/dhcp_server.graphql35
-rw-r--r--src/services/api/graphql/graphql/schema/interface_ethernet.graphql18
-rw-r--r--src/services/api/graphql/graphql/schema/schema.graphql18
-rw-r--r--src/services/api/graphql/recipes/__init__.py0
-rw-r--r--src/services/api/graphql/recipes/config_file.py16
-rw-r--r--src/services/api/graphql/recipes/dhcp_server.py13
-rw-r--r--src/services/api/graphql/recipes/interface_ethernet.py13
-rw-r--r--src/services/api/graphql/recipes/recipe.py68
-rw-r--r--src/services/api/graphql/recipes/templates/dhcp_server.tmpl9
-rw-r--r--src/services/api/graphql/recipes/templates/interface_ethernet.tmpl5
-rw-r--r--src/services/api/graphql/state.py4
-rwxr-xr-xsrc/services/vyos-configd7
-rwxr-xr-xsrc/services/vyos-http-api-server628
-rwxr-xr-xsrc/system/keepalived-fifo.py91
-rwxr-xr-xsrc/system/on-dhcp-event.sh25
-rw-r--r--src/systemd/vyos-http-api.service3
-rwxr-xr-xsrc/validators/allowed-vlan19
-rwxr-xr-xsrc/validators/base6427
-rwxr-xr-xsrc/validators/script9
321 files changed, 11080 insertions, 4994 deletions
diff --git a/Makefile b/Makefile
index e5a420601..a9c45253c 100644
--- a/Makefile
+++ b/Makefile
@@ -46,7 +46,11 @@ interface_definitions: $(config_xml_obj)
rm -f $(TMPL_DIR)/system/node.def
rm -f $(TMPL_DIR)/vpn/node.def
rm -f $(TMPL_DIR)/vpn/ipsec/node.def
- rm -rf $(TMPL_DIR)/vpn/nipsec
+
+ # XXX: T3781: migrate back to old iptables NAT implementation as we can not use nft
+ # which requires Kernel 5.10 for proper prefix translation support. Kernel 5.10
+ # unfortunately breaks with Intel QAT :(
+ rm -rf $(TMPL_DIR)/nat
# XXX: required until OSPF and RIP is migrated from vyatta-cfg-quagga to vyos-1x
mkdir $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6
@@ -85,21 +89,25 @@ op_mode_definitions: $(op_xml_obj)
rm -f $(OP_TMPL_DIR)/show/system/node.def
rm -f $(OP_TMPL_DIR)/show/vpn/node.def
+ # XXX: T3781: migrate back to old iptables NAT implementation as we can not use nft
+ # which requires Kernel 5.10 for proper prefix translation support. Kernel 5.10
+ # unfortunately breaks with Intel QAT :(
+ rm -rf $(OP_TMPL_DIR)/show/nat
+
# XXX: ping must be able to recursivly call itself as the
# options are provided from the script itself
ln -s ../node.tag $(OP_TMPL_DIR)/ping/node.tag/node.tag/
-.PHONY: component_versions
-.ONESHELL:
-component_versions: interface_definitions
- $(CURDIR)/scripts/build-component-versions $(BUILD_DIR)/interface-definitions $(DATA_DIR)
+ # XXX: test if there are empty node.def files - this is not allowed as these
+ # could mask help strings or mandatory priority statements
+ find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1'
.PHONY: vyshim
vyshim:
$(MAKE) -C $(SHIM_DIR)
.PHONY: all
-all: clean interface_definitions op_mode_definitions component_versions vyshim
+all: clean interface_definitions op_mode_definitions vyshim
.PHONY: clean
clean:
diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl
index 070a966b7..a2a2382fa 100644
--- a/data/templates/accel-ppp/l2tp.config.tmpl
+++ b/data/templates/accel-ppp/l2tp.config.tmpl
@@ -57,6 +57,9 @@ bind={{ outside_addr }}
{% if lns_shared_secret %}
secret={{ lns_shared_secret }}
{% endif %}
+{% if lns_host_name %}
+host-name={{ lns_host_name }}
+{% endif %}
[client-ip-range]
0.0.0.0/0
diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.tmpl
index 5a6cfe749..3cfc4a906 100644
--- a/data/templates/accel-ppp/pptp.config.tmpl
+++ b/data/templates/accel-ppp/pptp.config.tmpl
@@ -2,12 +2,13 @@
[modules]
log_syslog
pptp
-ippool
+shaper
{% if auth_mode == 'local' %}
chap-secrets
{% elif auth_mode == 'radius' %}
radius
{% endif %}
+ippool
{% for proto in auth_proto %}
{{proto}}
{% endfor %}
@@ -87,6 +88,10 @@ nas-ip-address={{ radius_nas_ip }}
bind={{ radius_source_address }}
{% endif %}
{% endif %}
+{# Both chap-secrets and radius block required the gw-ip-address #}
+{% if gw_ip is defined and gw_ip is not none %}
+gw-ip-address={{ gw_ip }}
+{% endif %}
[cli]
tcp=127.0.0.1:2003
diff --git a/data/templates/accel-ppp/sstp.config.tmpl b/data/templates/accel-ppp/sstp.config.tmpl
index 7ca7b1c1e..7a40a96aa 100644
--- a/data/templates/accel-ppp/sstp.config.tmpl
+++ b/data/templates/accel-ppp/sstp.config.tmpl
@@ -29,7 +29,9 @@ disable
verbose=1
ifname=sstp%d
accept=ssl
+{% if ssl.ca_cert_file is defined and ssl.ca_cert_file is not none %}
ssl-ca-file={{ ssl.ca_cert_file }}
+{% endif %}
ssl-pemfile={{ ssl.cert_file }}
ssl-keyfile={{ ssl.key_file }}
@@ -50,9 +52,9 @@ verbose=1
check-ip=1
{# MTU #}
mtu={{ mtu }}
-{% if client_ipv6_pool is defined %}
-ipv6=allow
-{% endif %}
+ipv6={{ 'allow' if ppp_options.ipv6 == "deny" and client_ipv6_pool is defined else ppp_options.ipv6 }}
+ipv4={{ ppp_options.ipv4 }}
+
mppe={{ ppp_options.mppe }}
lcp-echo-interval={{ ppp_options.lcp_echo_interval }}
lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }}
diff --git a/data/templates/conserver/dropbear@.service.tmpl b/data/templates/conserver/dropbear@.service.tmpl
index 4bb73f751..e355dab43 100644
--- a/data/templates/conserver/dropbear@.service.tmpl
+++ b/data/templates/conserver/dropbear@.service.tmpl
@@ -1,4 +1,4 @@
[Service]
ExecStart=
-ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console {{ device }}" -P /run/conserver/dropbear.%I.pid -p %I
+ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -b /etc/issue.net -c "/usr/bin/console {{ device }}" -P /run/conserver/dropbear.%I.pid -p %I
PIDFile=/run/conserver/dropbear.%I.pid
diff --git a/data/templates/dhcp-client/ipv4.tmpl b/data/templates/dhcp-client/ipv4.tmpl
index c934b7cdb..fcc2846a5 100644
--- a/data/templates/dhcp-client/ipv4.tmpl
+++ b/data/templates/dhcp-client/ipv4.tmpl
@@ -2,7 +2,8 @@
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
timeout 60;
-retry 300;
+retry 60;
+initial-interval 2;
interface "{{ ifname }}" {
send host-name "{{ dhcp_options.host_name }}";
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl
index ff2e31998..233e2cc53 100644
--- a/data/templates/dhcp-server/dhcpd.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -8,16 +8,12 @@
on release {
set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
- set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
- set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
- execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", "", ClientIp, "", "");
}
on expiry {
set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
- set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
- set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
- execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", "", ClientIp, "", "");
}
{% endif %}
@@ -35,32 +31,24 @@ option wpad-url code 252 = text;
{% endfor %}
{% endif %}
-{% if shared_network_name is defined and shared_network_name is not none %}
-{% for network, network_config in shared_network_name.items() if network_config.disable is not defined %}
-{% if network_config.subnet is defined and network_config.subnet is not none %}
-{% for subnet, subnet_config in network_config.subnet.items() %}
-{% if subnet_config.failover is defined and subnet_config.failover is defined and subnet_config.failover.name is defined and subnet_config.failover.name is not none %}
-# Failover configuration for {{ subnet }}
-failover peer "{{ subnet_config.failover.name }}" {
-{% if subnet_config.failover.status == 'primary' %}
+{% if failover is defined and failover is not none %}
+# DHCP failover configuration
+failover peer "{{ failover.name }}" {
+{% if failover.status == 'primary' %}
primary;
mclt 1800;
split 128;
-{% elif subnet_config.failover.status == 'secondary' %}
+{% elif failover.status == 'secondary' %}
secondary;
-{% endif %}
- address {{ subnet_config.failover.local_address }};
+{% endif %}
+ address {{ failover.source_address }};
port 520;
- peer address {{ subnet_config.failover.peer_address }};
+ peer address {{ failover.remote }};
peer port 520;
max-response-delay 30;
max-unacked-updates 10;
load balance max seconds 3;
}
-{% endif %}
-{% endfor %}
-{% endif %}
-{% endfor %}
{% endif %}
{% if listen_address is defined and listen_address is not none %}
@@ -78,6 +66,21 @@ shared-network {{ network | replace('_','-') }} {
{% if network_config.authoritative is defined %}
authoritative;
{% endif %}
+{% if network_config.name_server is defined and network_config.name_server is not none %}
+ option domain-name-servers {{ network_config.name_server | join(', ') }};
+{% endif %}
+{% if network_config.domain_name is defined and network_config.domain_name is not none %}
+ option domain-name "{{ network_config.domain_name }}";
+{% endif %}
+{% if network_config.domain_search is defined and network_config.domain_search is not none %}
+ option domain-search "{{ network_config.domain_search | join('", "') }}";
+{% endif %}
+{% if network_config.ntp_server is defined and network_config.ntp_server is not none %}
+ option ntp-servers {{ network_config.ntp_server | join(', ') }};
+{% endif %}
+{% if network_config.ping_check is defined %}
+ ping-check true;
+{% endif %}
{% if network_config.shared_network_parameters is defined and network_config.shared_network_parameters is not none %}
# The following {{ network_config.shared_network_parameters | length }} line(s)
# were added as shared-network-parameters in the CLI and have not been validated
@@ -87,9 +90,15 @@ shared-network {{ network | replace('_','-') }} {
{% endif %}
{% if network_config.subnet is defined and network_config.subnet is not none %}
{% for subnet, subnet_config in network_config.subnet.items() %}
+{% if subnet_config.description is defined and subnet_config.description is not none %}
+ # {{ subnet_config.description }}
+{% endif %}
subnet {{ subnet | address_from_cidr }} netmask {{ subnet | netmask_from_cidr }} {
-{% if subnet_config.dns_server is defined and subnet_config.dns_server is not none %}
- option domain-name-servers {{ subnet_config.dns_server | join(', ') }};
+{% if subnet_config.name_server is defined and subnet_config.name_server is not none %}
+ option domain-name-servers {{ subnet_config.name_server | join(', ') }};
+{% endif %}
+{% if subnet_config.domain_name is defined and subnet_config.domain_name is not none %}
+ option domain-name "{{ subnet_config.domain_name }}";
{% endif %}
{% if subnet_config.domain_search is defined and subnet_config.domain_search is not none %}
option domain-search "{{ subnet_config.domain_search | join('", "') }}";
@@ -114,9 +123,13 @@ shared-network {{ network | replace('_','-') }} {
{% if subnet_config.default_router and subnet_config.default_router is not none %}
{% set static_default_route = ', ' + '0.0.0.0/0' | isc_static_route(subnet_config.default_router) %}
{% endif %}
-{% if subnet_config.static_route.router is defined and subnet_config.static_route.router is not none and subnet_config.static_route.destination_subnet is defined and subnet_config.static_route.destination_subnet is not none %}
- option rfc3442-static-route {{ subnet_config.static_route.destination_subnet | isc_static_route(subnet_config.static_route.router) }}{{ static_default_route }};
- option windows-static-route {{ subnet_config.static_route.destination_subnet | isc_static_route(subnet_config.static_route.router) }};
+{% if subnet_config.static_route is defined and subnet_config.static_route is not none %}
+{% set rfc3442_routes = [] %}
+{% for route, route_options in subnet_config.static_route.items() %}
+{% set rfc3442_routes = rfc3442_routes.append(route | isc_static_route(route_options.next_hop)) %}
+{% endfor %}
+ option rfc3442-static-route {{ rfc3442_routes | join(', ') }}{{ static_default_route }};
+ option windows-static-route {{ rfc3442_routes | join(', ') }};
{% endif %}
{% endif %}
{% if subnet_config.ip_forwarding is defined %}
@@ -128,9 +141,6 @@ shared-network {{ network | replace('_','-') }} {
{% if subnet_config.server_identifier is defined and subnet_config.server_identifier is not none %}
option dhcp-server-identifier {{ subnet_config.server_identifier }};
{% endif %}
-{% if subnet_config.domain_name is defined and subnet_config.domain_name is not none %}
- option domain-name "{{ subnet_config.domain_name }}";
-{% endif %}
{% if subnet_config.subnet_parameters is defined and subnet_config.subnet_parameters is not none %}
# The following {{ subnet_config.subnet_parameters | length }} line(s) were added as
# subnet-parameters in the CLI and have not been validated!!!
@@ -161,6 +171,9 @@ shared-network {{ network | replace('_','-') }} {
default-lease-time {{ subnet_config.lease }};
max-lease-time {{ subnet_config.lease }};
{% endif %}
+{% if network_config.ping_check is not defined and subnet_config.ping_check is defined %}
+ ping-check true;
+{% endif %}
{% if subnet_config.static_mapping is defined and subnet_config.static_mapping is not none %}
{% for host, host_config in subnet_config.static_mapping.items() if host_config.disable is not defined %}
host {{ host | replace('_','-') if host_decl_name is defined else network | replace('_','-') + '_' + host | replace('_','-') }} {
@@ -178,22 +191,22 @@ shared-network {{ network | replace('_','-') }} {
}
{% endfor %}
{% endif %}
-{% if subnet_config.failover is defined and subnet_config.failover.name is defined and subnet_config.failover.name is not none %}
+{% if subnet_config.range is defined and subnet_config.range is not none %}
+{# pool configuration can only be used if there follows a range option #}
pool {
- failover peer "{{ subnet_config.failover.name }}";
+{% endif %}
+{% if subnet_config.enable_failover is defined %}
+ failover peer "{{ failover.name }}";
deny dynamic bootp clients;
+{% endif %}
{% if subnet_config.range is defined and subnet_config.range is not none %}
{% for range, range_options in subnet_config.range.items() %}
range {{ range_options.start }} {{ range_options.stop }};
{% endfor %}
{% endif %}
+{% if subnet_config.range is defined and subnet_config.range is not none %}
+{# pool configuration can only be used if there follows a range option #}
}
-{% else %}
-{% if subnet_config.range is defined and subnet_config.range is not none %}
-{% for range, range_options in subnet_config.range.items() %}
- range {{ range_options.start }} {{ range_options.stop }};
-{% endfor %}
-{% endif %}
{% endif %}
}
{% endfor %}
@@ -201,11 +214,15 @@ shared-network {{ network | replace('_','-') }} {
on commit {
set shared-networkname = "{{ network | replace('_','-') }}";
{% if hostfile_update is defined %}
- set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
set ClientIp = binary-to-ascii(10, 8, ".", leased-address);
set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
- set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
- execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain);
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name, "empty_hostname");
+ if not (ClientName = "empty_hostname") {
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain);
+ } else {
+ log(concat("Hostname is not defined for client with IP: ", ClientIP, " MAC: ", ClientMac));
+ }
{% endif %}
}
}
diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl
index 8799718b0..be0778993 100644
--- a/data/templates/dns-forwarding/recursor.conf.tmpl
+++ b/data/templates/dns-forwarding/recursor.conf.tmpl
@@ -10,8 +10,7 @@ threads=1
allow-from={{ allow_from | join(',') }}
log-common-errors=yes
non-local-bind=yes
-query-local-address={{ source_address_v4 | join(',') }}
-query-local-address6={{ source_address_v6 | join(',') }}
+query-local-address={{ source_address | join(',') }}
lua-config-file=recursor.conf.lua
# cache-size
@@ -29,5 +28,14 @@ local-address={{ listen_address | join(',') }}
# dnssec
dnssec={{ dnssec }}
+{# dns: T3277: #}
+{% if no_serve_rfc1918 is defined %}
+# serve-rfc1918
+serve-rfc1918=no
+{% else %}
+# serve-rfc1918
+serve-rfc1918=yes
+{% endif %}
+
forward-zones-file=recursor.forward-zones.conf
diff --git a/data/templates/frr/isisd.frr.tmpl b/data/templates/frr/isisd.frr.tmpl
index 8a813d9cb..6cfa076d0 100644
--- a/data/templates/frr/isisd.frr.tmpl
+++ b/data/templates/frr/isisd.frr.tmpl
@@ -1,5 +1,5 @@
!
-router isis {{ process }}
+router isis VyOS {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
net {{ net }}
{% if dynamic_hostname is defined %}
hostname dynamic
@@ -13,8 +13,15 @@ router isis {{ process }}
{% if set_overload_bit is defined %}
set-overload-bit
{% endif %}
-{% if domain_password is defined and domain_password.plaintext_password is defined and domain_password.plaintext_password is not none %}
+{% if domain_password is defined and domain_password is not none %}
+{% if domain_password.md5 is defined and domain_password.md5 is not none %}
+ domain-password md5 {{ domain_password.plaintext_password }}
+{% elif domain_password.plaintext_password is defined and domain_password.plaintext_password is not none %}
domain-password clear {{ domain_password.plaintext_password }}
+{% endif %}
+{% endif %}
+{% if log_adjacency_changes is defined %}
+ log-adjacency-changes
{% endif %}
{% if lsp_gen_interval is defined and lsp_gen_interval is not none %}
lsp-gen-interval {{ lsp_gen_interval }}
@@ -95,47 +102,61 @@ router isis {{ process }}
{% if spf_delay_ietf is defined and spf_delay_ietf.init_delay is defined and spf_delay_ietf.init_delay is not none %}
spf-delay-ietf init-delay {{ spf_delay_ietf.init_delay }}
{% endif %}
-{% if area_password is defined and area_password.md5 is defined and area_password.md5 is not none %}
+{% if area_password is defined and area_password is not none %}
+{% if area_password.md5 is defined and area_password.md5 is not none %}
area-password md5 {{ area_password.md5 }}
-{% elif area_password is defined and area_password.plaintext_password is defined and area_password.plaintext_password is not none %}
+{% elif area_password.plaintext_password is defined and area_password.plaintext_password is not none %}
area-password clear {{ area_password.plaintext_password }}
+{% endif %}
{% endif %}
{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %}
-{% for level in default_information.originate.ipv4 if default_information.originate.ipv4 is defined %}
- default-information originate ipv4 {{ level | replace('_', '-') }}
-{% endfor %}
-{% for level in default_information.originate.ipv6 if default_information.originate.ipv6 is defined %}
- default-information originate ipv6 {{ level | replace('_', '-') }} always
+{% for afi, afi_config in default_information.originate.items() %}
+{% for level, level_config in afi_config.items() %}
+ default-information originate {{ afi }} {{ level | replace('_', '-') }} {{ 'always' if level_config.always is defined }} {{ 'route-map ' ~ level_config.route_map if level_config.route_map is defined }} {{ 'metric ' ~ level_config.metric if level_config.metric is defined }}
+{% endfor %}
{% endfor %}
{% endif %}
-{% if redistribute is defined and redistribute.ipv4 is defined and redistribute.ipv4 is not none %}
-{% for protocol in redistribute.ipv4 %}
-{% for level, level_config in redistribute.ipv4[protocol].items() %}
-{% if level_config.metric is defined and level_config.metric is not none %}
+{% if redistribute is defined %}
+{% if redistribute.ipv4 is defined and redistribute.ipv4 is not none %}
+{% for protocol, protocol_options in redistribute.ipv4.items() %}
+{% for level, level_config in protocol_options.items() %}
+{% if level_config.metric is defined and level_config.metric is not none %}
redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }} metric {{ level_config.metric }}
-{% elif level_config.route_map is defined and level_config.route_map is not none %}
+{% elif level_config.route_map is defined and level_config.route_map is not none %}
redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }} route-map {{ level_config.route_map }}
-{% else %}
+{% else %}
redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }}
-{% endif %}
+{% endif %}
+{% endfor %}
{% endfor %}
-{% endfor %}
+{% endif %}
+{% if redistribute.ipv6 is defined and redistribute.ipv6 is not none %}
+{% for protocol, protocol_options in redistribute.ipv6.items() %}
+{% for level, level_config in protocol_options.items() %}
+{% if level_config.metric is defined and level_config.metric is not none %}
+ redistribute ipv6 {{ protocol }} {{ level | replace('_', '-') }} metric {{ level_config.metric }}
+{% elif level_config.route_map is defined and level_config.route_map is not none %}
+ redistribute ipv6 {{ protocol }} {{ level | replace('_', '-') }} route-map {{ level_config.route_map }}
+{% else %}
+ redistribute ipv6 {{ protocol }} {{ level | replace('_', '-') }}
+{% endif %}
+{% endfor %}
+{% endfor %}
+{% endif %}
{% endif %}
{% if level is defined and level is not none %}
-{% if level == 'level-1' %}
- is-type level-1
-{% elif level == 'level-2' %}
+{% if level == 'level-2' %}
is-type level-2-only
-{% elif level == 'level-1-2' %}
- is-type level-1-2
+{% else %}
+ is-type {{ level }}
{% endif %}
{% endif %}
!
{% if interface is defined and interface is not none %}
{% for iface, iface_config in interface.items() %}
-interface {{ iface }}
- ip router isis {{ process }}
- ipv6 router isis {{ process }}
+interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
+ ip router isis VyOS
+ ipv6 router isis VyOS
{% if iface_config.bfd is defined %}
isis bfd
{% endif %}
@@ -174,3 +195,4 @@ interface {{ iface }}
{% endif %}
{% endfor %}
{% endif %}
+! \ No newline at end of file
diff --git a/data/templates/frr/route-map.frr.tmpl b/data/templates/frr/route-map.frr.tmpl
new file mode 100644
index 000000000..6b33cc126
--- /dev/null
+++ b/data/templates/frr/route-map.frr.tmpl
@@ -0,0 +1,5 @@
+!
+{% if route_map is defined and route_map is not none %}
+ip protocol {{ protocol }} route-map {{ route_map }}
+{% endif %}
+!
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
index 4aaf0132f..d25e5193a 100644
--- a/data/templates/https/nginx.default.tmpl
+++ b/data/templates/https/nginx.default.tmpl
@@ -38,11 +38,14 @@ server {
#
include snippets/snakeoil.conf;
{% endif %}
+ ssl_protocols TLSv1.2 TLSv1.3;
# proxy settings for HTTP API, if enabled; 503, if not
- location ~ /(retrieve|configure|config-file|image|generate|show) {
+ location ~ /(retrieve|configure|config-file|image|generate|show|docs|openapi.json|redoc|graphql) {
{% if server.api %}
proxy_pass http://localhost:{{ server.api.port }};
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600;
proxy_buffering off;
{% else %}
diff --git a/data/templates/lcd/LCDd.conf.tmpl b/data/templates/lcd/LCDd.conf.tmpl
index 6cf6a440f..2c7ad920f 100644
--- a/data/templates/lcd/LCDd.conf.tmpl
+++ b/data/templates/lcd/LCDd.conf.tmpl
@@ -53,6 +53,8 @@ DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/
Driver=CFontzPacket
{% elif model == 'sdec' %}
Driver=sdeclcd
+{% elif model == 'hd44780' %}
+Driver=hd44780
{% endif %}
{% endif %}
@@ -128,5 +130,10 @@ USB=yes
## SDEC driver for Lanner, Watchguard, Sophos sppliances ##
[sdeclcd]
# No options
+{% elif model == 'hd44780' %}
+[hd44780]
+ConnectionType=ezio
+Device={{ device }}
+Size=16x2
{% endif %}
{% endif %}
diff --git a/data/templates/ntp/ntpd.conf.tmpl b/data/templates/ntp/ntpd.conf.tmpl
index 2b56b53c3..38e68f24f 100644
--- a/data/templates/ntp/ntpd.conf.tmpl
+++ b/data/templates/ntp/ntpd.conf.tmpl
@@ -6,6 +6,8 @@
driftfile /var/lib/ntp/ntp.drift
# By default, only allow ntpd to query time sources, ignore any incoming requests
restrict default noquery nopeer notrap nomodify
+# Allow pool associations
+restrict source nomodify notrap noquery
# Local users have unrestricted access, allowing reconfiguration via ntpdc
restrict 127.0.0.1
restrict -6 ::1
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index c5d665c0b..c2b0c2ef9 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -7,8 +7,6 @@
#
verb 3
-user {{ daemon_user }}
-group {{ daemon_group }}
dev-type {{ device_type }}
dev {{ ifname }}
persist-key
@@ -74,6 +72,16 @@ topology {{ server.topology }}
{% for subnet in server.subnet %}
{% if subnet | is_ipv4 %}
server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool
+{# First ip address is used as gateway. It's allows to use metrics #}
+{% if server.push_route is defined and server.push_route is not none %}
+{% for route, route_config in server.push_route.items() %}
+{% if route | is_ipv4 %}
+push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}{% if route_config.metric is defined %} {{ subnet | first_host_address }} {{ route_config.metric }}{% endif %}"
+{% elif route | is_ipv6 %}
+push "route-ipv6 {{ route }}"
+{% endif %}
+{% endfor %}
+{% endif %}
{# 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' %}
@@ -106,15 +114,6 @@ management /run/openvpn/openvpn-mgmt-intf unix
ccd-exclusive
{% endif %}
-{% if server.push_route is defined and server.push_route is not none %}
-{% for route in server.push_route %}
-{% if route | is_ipv4 %}
-push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}"
-{% elif route | is_ipv6 %}
-push "route-ipv6 {{ route }}"
-{% endif %}
-{% endfor %}
-{% endif %}
{% if server.name_server is defined and server.name_server is not none %}
{% for nameserver in server.name_server %}
{% if nameserver | is_ipv4 %}
@@ -258,16 +257,3 @@ auth {{ hash }}
auth-user-pass {{ auth_user_pass_file }}
auth-retry nointeract
{% endif %}
-
-{% if openvpn_option is defined and openvpn_option is not none %}
-#
-# Custom options added by user (not validated)
-#
-{% for option in openvpn_option %}
-{% for argument in option.split('--') %}
-{% if argument is defined and argument != '' %}
---{{ argument }}
-{% endif %}
-{% endfor %}
-{% endfor %}
-{% endif %}
diff --git a/data/templates/openvpn/service-override.conf.tmpl b/data/templates/openvpn/service-override.conf.tmpl
new file mode 100644
index 000000000..069bdbd08
--- /dev/null
+++ b/data/templates/openvpn/service-override.conf.tmpl
@@ -0,0 +1,20 @@
+[Service]
+ExecStart=
+ExecStart=/usr/sbin/openvpn --daemon openvpn-%i --config %i.conf --status %i.status 30 --writepid %i.pid
+{%- if openvpn_option is defined and openvpn_option is not none %}
+{% for option in openvpn_option %}
+{# Remove the '--' prefix from variable if it is presented #}
+{% if option.startswith('--') %}
+{% set option = option.split('--', maxsplit=1)[1] %}
+{% endif %}
+{# Workaround to pass '--push' options properly. Previously openvpn accepted this option without values in double-quotes #}
+{# But now it stopped doing this, so we need to add them for compatibility #}
+{# HOWEVER! This is a raw option and we do not promise that this or any other trick will work for all the cases. #}
+{# Using 'openvpn-option' you take all responsibility for compatibility for yourself. #}
+{% if option.startswith('push') and not (option.startswith('push "') and option.endswith('"')) %}
+{% set option = 'push \"%s\"'|format(option.split('push ', maxsplit=1)[1]) %}
+{% endif %}
+ --{{ option }}
+{%- endfor %}
+{% endif %}
+
diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl
index 0f78f9384..f416f9947 100644
--- a/data/templates/pppoe/peer.tmpl
+++ b/data/templates/pppoe/peer.tmpl
@@ -1,8 +1,5 @@
### Autogenerated by interfaces-pppoe.py ###
-
-{% if description %}
-# {{ description }}
-{% endif %}
+{{ '# ' ~ description if description is defined else '' }}
# Require peer to provide the local IP address if it is not
# specified explicitly in the config file.
@@ -37,8 +34,14 @@ noproxyarp
# Unlimited connection attempts
maxfail 0
-plugin rp-pppoe.so
-{{ source_interface }}
+plugin rp-pppoe.so {{ source_interface }}
+{% if access_concentrator is defined and access_concentrator is not none %}
+rp_pppoe_ac '{{ access_concentrator }}'
+{% endif %}
+{% if service_name is defined and service_name is not none %}
+rp_pppoe_service '{{ service_name }}'
+{% endif %}
+
persist
ifname {{ ifname }}
ipparam {{ ifname }}
@@ -60,10 +63,6 @@ ipv6cp-use-ipaddr
{% endif %}
{% endif %}
-{% if service_name is defined %}
-rp_pppoe_service "{{ service_name }}"
-{% endif %}
-
{% if connect_on_demand is defined %}
demand
# See T2249. PPP default route options should only be set when in on-demand
diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl
index 10fbb9d3c..e25ef48d4 100644
--- a/data/templates/syslog/rsyslog.conf.tmpl
+++ b/data/templates/syslog/rsyslog.conf.tmpl
@@ -2,47 +2,47 @@
## file based logging
{% if files['global']['marker'] %}
$ModLoad immark
-{% if files['global']['marker-interval'] %}
+{% if files['global']['marker-interval'] %}
$MarkMessagePeriod {{files['global']['marker-interval']}}
-{% endif %}
+{% endif %}
{% endif %}
{% if files['global']['preserver_fqdn'] %}
$PreserveFQDN on
{% endif %}
-{% for file in files %}
-$outchannel {{file}},{{files[file]['log-file']}},{{files[file]['max-size']}},{{files[file]['action-on-max-size']}}
-{{files[file]['selectors']}} :omfile:${{file}}
+{% for file, file_options in files.items() %}
+$outchannel {{ file }},{{ file_options['log-file'] }},{{ file_options['max-size'] }},{{ file_options['action-on-max-size'] }}
+{{ file_options['selectors'] }} :omfile:${{ file }}
{% endfor %}
-{% if console %}
+{% if console is defined and console is not none %}
## console logging
-{% for con in console %}
-{{console[con]['selectors']}} /dev/console
-{% endfor %}
+{% for con, con_options in console.items() %}
+{{ con_options['selectors'] }} /dev/console
+{% endfor %}
{% endif %}
-{% if hosts %}
+{% if hosts is defined and hosts is not none %}
## remote logging
-{% for host in hosts %}
-{% if hosts[host]['proto'] == 'tcp' %}
-{% if hosts[host]['port'] %}
-{% if hosts[host]['oct_count'] %}
-{{hosts[host]['selectors']}} @@(o){{host}}:{{hosts[host]['port']}};RSYSLOG_SyslogProtocol23Format
+{% for host, host_options in hosts.items() %}
+{% if host_options.proto == 'tcp' %}
+{% if host_options.port is defined %}
+{% if host_options.oct_count is defined %}
+{{ host_options.selectors }} @@(o){{ host }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format
+{% else %}
+{{ host_options.selectors }} @@{{ host }}:{{ host_options.port }}
+{% endif %}
{% else %}
-{{hosts[host]['selectors']}} @@{{host}}:{{hosts[host]['port']}}
+{{ host_options.selectors }} @@{{ host }}
{% endif %}
{% else %}
-{{hosts[host]['selectors']}} @@{{host}}
-{% endif %}
-{% else %}
-{% if hosts[host]['port'] %}
-{{hosts[host]['selectors']}} @{{host}}:{{hosts[host]['port']}}
-{% else %}
-{{hosts[host]['selectors']}} @{{host}}
+{% if host_options['port'] %}
+{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }}
+{% else %}
+{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}
+{% endif %}
{% endif %}
-{% endif %}
-{% endfor %}
+{% endfor %}
{% endif %}
-{% if user %}
-{% for u in user %}
-{{user[u]['selectors']}} :omusrmsg:{{u}}
-{% endfor %}
+{% if user is defined and user is not none %}
+{% for username, user_options in user.items() %}
+{{ user_options.selectors }} :omusrmsg:{{ username }}
+{% endfor %}
{% endif %}
diff --git a/data/templates/vrrp/daemon.tmpl b/data/templates/vrrp/daemon.tmpl
deleted file mode 100644
index c9dbea72d..000000000
--- a/data/templates/vrrp/daemon.tmpl
+++ /dev/null
@@ -1,5 +0,0 @@
-# Autogenerated by VyOS
-# Options to pass to keepalived
-
-# DAEMON_ARGS are appended to the keepalived command-line
-DAEMON_ARGS="--snmp"
diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl
index d51522e45..7b8f7cb09 100644
--- a/data/templates/vrrp/keepalived.conf.tmpl
+++ b/data/templates/vrrp/keepalived.conf.tmpl
@@ -5,101 +5,102 @@
global_defs {
dynamic_interfaces
script_user root
- notify_fifo /run/keepalived_notify_fifo
+ # Don't run scripts configured to be run as root if any part of the path
+ # is writable by a non-root user.
+ enable_script_security
+ notify_fifo /run/keepalived/keepalived_notify_fifo
notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
}
-{% for group in groups %}
-
-{% if group.health_check_script %}
-vrrp_script healthcheck_{{ group.name }} {
- script "{{ group.health_check_script }}"
- interval {{ group.health_check_interval }}
- fall {{ group.health_check_count }}
+{% if group is defined and group is not none %}
+{% for name, group_config in group.items() if group_config.disable is not defined %}
+{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %}
+vrrp_script healthcheck_{{ name }} {
+ script "{{ group_config.health_check.script }}"
+ interval {{ group_config.health_check.interval }}
+ fall {{ group_config.health_check.failure_count }}
rise 1
-
}
-{% endif %}
-
-vrrp_instance {{ group.name }} {
- {% if group.description %}
- # {{ group.description }}
- {% endif %}
-
+{% endif %}
+vrrp_instance {{ name }} {
+{% if group_config.description is defined and group_config.description is not none %}
+ # {{ group_config.description }}
+{% endif %}
state BACKUP
- interface {{ group.interface }}
- virtual_router_id {{ group.vrid }}
- priority {{ group.priority }}
- advert_int {{ group.advertise_interval }}
-
- {% if group.preempt %}
- preempt_delay {{ group.preempt_delay }}
- {% else %}
+ interface {{ group_config.interface }}
+ virtual_router_id {{ group_config.vrid }}
+ priority {{ group_config.priority }}
+ advert_int {{ group_config.advertise_interval }}
+{% if group_config.no_preempt is not defined and group_config.preempt_delay is defined and group_config.preempt_delay is not none %}
+ preempt_delay {{ group_config.preempt_delay }}
+{% elif group_config.no_preempt is defined %}
nopreempt
- {% endif %}
-
- {% if group.peer_address %}
- unicast_peer { {{ group.peer_address }} }
- {% endif %}
-
- {% if group.hello_source %}
- {% if group.peer_address %}
- unicast_src_ip {{ group.hello_source }}
- {% else %}
- mcast_src_ip {{ group.hello_source }}
- {% endif %}
- {% endif %}
-
- {% if group.use_vmac and group.peer_address %}
- use_vmac {{group.interface}}v{{group.vrid}}
- vmac_xmit_base
- {% elif group.use_vmac %}
- use_vmac {{group.interface}}v{{group.vrid}}
- {% endif %}
-
- {% if group.auth_password %}
- authentication {
- auth_pass "{{ group.auth_password }}"
- auth_type {{ group.auth_type }}
- }
- {% endif %}
-
+{% endif %}
+{% if group_config.peer_address is defined and group_config.peer_address is not none %}
+ unicast_peer { {{ group_config.peer_address }} }
+{% endif %}
+{% if group_config.hello_source_address is defined and group_config.hello_source_address is not none %}
+{% if group_config.peer_address is defined and group_config.peer_address is not none %}
+ unicast_src_ip {{ group_config.hello_source_address }}
+{% else %}
+ mcast_src_ip {{ group_config.hello_source_address }}
+{% endif %}
+{% endif %}
+{% if group_config.rfc3768_compatibility is defined and group_config.peer_address is defined %}
+ use_vmac {{ group_config.interface }}v{{ group_config.vrid }}
+ vmac_xmit_base
+{% elif group_config.rfc3768_compatibility is defined %}
+ use_vmac {{ group_config.interface }}v{{ group_config.vrid }}
+{% endif %}
+{% if group_config.authentication is defined and group_config.authentication is not none %}
+ authentication {
+ auth_pass "{{ group_config.authentication.password }}"
+{% if group_config.authentication.type == 'plaintext-password' %}
+ auth_type PASS
+{% else %}
+ auth_type {{ group_config.authentication.type | upper }}
+{% endif %}
+ }
+{% endif %}
+{% if group_config.virtual_address is defined and group_config.virtual_address is not none %}
virtual_ipaddress {
- {% for addr in group.virtual_addresses %}
+{% for addr in group_config.virtual_address %}
{{ addr }}
- {% endfor %}
+{% endfor %}
}
-
- {% if group.virtual_addresses_excluded %}
+{% endif %}
+{% if group_config.virtual_address_excluded is defined and group_config.virtual_address_excluded is not none %}
virtual_ipaddress_excluded {
- {% for addr in group.virtual_addresses_excluded %}
+{% for addr in group_config.virtual_address_excluded %}
{{ addr }}
- {% endfor %}
+{% endfor %}
}
- {% endif %}
-
- {% if group.health_check_script %}
+{% endif %}
+{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %}
track_script {
- healthcheck_{{ group.name }}
+ healthcheck_{{ name }}
}
- {% endif %}
+{% endif %}
}
+{% endfor %}
+{% endif %}
-{% endfor %}
-
-{% for sync_group in sync_groups %}
-vrrp_sync_group {{ sync_group.name }} {
- group {
- {% for member in sync_group.members %}
- {{ member }}
- {% endfor %}
- }
-
- {% if sync_group.conntrack_sync %}
- notify_master "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh master {{ sync_group.name }}"
- notify_backup "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh backup {{ sync_group.name }}"
- notify_fault "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh fault {{ sync_group.name }}"
- {% endif %}
+{% if sync_group is defined and sync_group is not none %}
+{% for name, group_config in sync_group.items() if group_config.disable is not defined %}
+vrrp_sync_group {{ name }} {
+ group {
+{% if group_config.member is defined and group_config.member is not none %}
+{% for member in group_config.member %}
+ {{ member }}
+{% endfor %}
+{% endif %}
+ }
+{% if conntrack_sync_group is defined and conntrack_sync_group == name %}
+{% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %}
+ notify_master "{{ vyos_helper }} master {{ name }}"
+ notify_backup "{{ vyos_helper }} backup {{ name }}"
+ notify_fault "{{ vyos_helper }} fault {{ name }}"
+{% endif %}
}
-
-{% endfor %}
+{% endfor %}
+{% endif %}
diff --git a/debian/control b/debian/control
index 2a107c954..8cafd8257 100644
--- a/debian/control
+++ b/debian/control
@@ -65,6 +65,7 @@ Depends:
iw,
keepalived (>=2.0.5),
lcdproc,
+ lcdproc-extra-drivers,
libatomic1,
libndp-tools,
libpam-radius-auth (>= 1.5.0),
@@ -140,6 +141,7 @@ Depends:
usbutils,
vyatta-bash,
vyatta-cfg,
+ vyos-http-api-tools,
vyos-utils,
wide-dhcpv6-client,
wireguard-tools,
diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index c075db898..0c0c203ea 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -1,3 +1,4 @@
+etc/cron.d
etc/dhcp
etc/netplug
etc/ppp
diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in
index 1b354d885..a0f73a03b 100644
--- a/interface-definitions/bcast-relay.xml.in
+++ b/interface-definitions/bcast-relay.xml.in
@@ -1,5 +1,4 @@
<?xml version="1.0"?>
-<!-- UDP broadcast relay configuration -->
<interfaceDefinition>
<node name="service">
<children>
@@ -14,8 +13,8 @@
<properties>
<help>Unique ID for each UDP port to forward</help>
<valueHelp>
- <format>1-99</format>
- <description>Numerical ID #</description>
+ <format>u32:1-99</format>
+ <description>Broadcast relay instance ID</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-99"/>
diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in
index 8c95239d9..0d485ef80 100644
--- a/interface-definitions/dhcp-relay.xml.in
+++ b/interface-definitions/dhcp-relay.xml.in
@@ -27,7 +27,7 @@
<properties>
<help>Policy to discard packets that have reached specified hop-count</help>
<valueHelp>
- <format>1-255</format>
+ <format>u32:1-255</format>
<description>Hop count (default: 10)</description>
</valueHelp>
<constraint>
@@ -41,7 +41,7 @@
<properties>
<help>Maximum packet size to send to a DHCPv4/BOOTP server</help>
<valueHelp>
- <format>64-1400</format>
+ <format>u32:64-1400</format>
<description>Maximum packet size (default: 576)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index 015500043..d1ed579e9 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -16,6 +16,55 @@
<valueless/>
</properties>
</leafNode>
+ <node name="failover">
+ <properties>
+ <help>DHCP failover configuration</help>
+ </properties>
+ <children>
+ #include <include/source-address-ipv4.xml.i>
+ <leafNode name="remote">
+ <properties>
+ <help>IPv4 remote address used for connectio</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of failover peer</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="name">
+ <properties>
+ <help>Peer name used to identify connection</help>
+ <constraint>
+ <regex>[-_a-zA-Z0-9.]+</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid failover peer name. May only contain letters, numbers and .-_</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="status">
+ <properties>
+ <help>Failover hierarchy</help>
+ <completionHelp>
+ <list>primary secondary</list>
+ </completionHelp>
+ <valueHelp>
+ <format>primary</format>
+ <description>Configure this server to be the primary node</description>
+ </valueHelp>
+ <valueHelp>
+ <format>secondary</format>
+ <description>Configure this server to be the secondary node</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(primary|secondary)$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid DHCP failover peer status</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="global-parameters">
<properties>
<help>Additional global parameters for DHCP server. You must
@@ -53,12 +102,13 @@
<valueless/>
</properties>
</leafNode>
- <leafNode name="description">
- <properties>
- <help>Shared-network-name description</help>
- </properties>
- </leafNode>
+ #include <include/dhcp/domain-name.xml.i>
+ #include <include/dhcp/domain-search.xml.i>
+ #include <include/dhcp/ntp-server.xml.i>
+ #include <include/dhcp/ping-check.xml.i>
+ #include <include/generic-description.xml.i>
#include <include/generic-disable-node.xml.i>
+ #include <include/name-server-ipv4.xml.i>
<leafNode name="shared-network-parameters">
<properties>
<help>Additional shared-network parameters for DHCP server.
@@ -96,7 +146,7 @@
<properties>
<help>Specifies the clients subnet mask as per RFC 950. If unset, subnet declaration is used.</help>
<valueHelp>
- <format>0-32</format>
+ <format>u32:0-32</format>
<description>DHCP client prefix length must be 0 to 32</description>
</valueHelp>
<constraint>
@@ -117,29 +167,16 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="dns-server">
+ #include <include/dhcp/domain-name.xml.i>
+ #include <include/dhcp/domain-search.xml.i>
+ #include <include/generic-description.xml.i>
+ #include <include/name-server-ipv4.xml.i>
+ <leafNode name="enable-failover">
<properties>
- <help>DNS server IPv4 address</help>
- <valueHelp>
- <format>ipv4</format>
- <description>DNS server IPv4 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="domain-name">
- <properties>
- <help>Client Domain Name</help>
- <constraint>
- <validator name="fqdn"/>
- </constraint>
- <constraintErrorMessage>Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_</constraintErrorMessage>
+ <help>Enable DHCP failover support for this subnet</help>
+ <valueless/>
</properties>
</leafNode>
- #include <include/dhcp-server-domain-search.xml.i>
<leafNode name="exclude">
<properties>
<help>IP address to exclude from DHCP lease range</help>
@@ -153,58 +190,6 @@
<multi/>
</properties>
</leafNode>
- <node name="failover">
- <properties>
- <help>DHCP failover parameters</help>
- </properties>
- <children>
- <leafNode name="local-address">
- <properties>
- <help>IP address for failover peer to connect [REQUIRED]</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address to exclude from lease range</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="name">
- <properties>
- <help>DHCP failover peer name [REQUIRED]</help>
- <constraint>
- <regex>[-_a-zA-Z0-9.]+</regex>
- </constraint>
- <constraintErrorMessage>Invalid failover peer name. May only contain letters, numbers and .-_</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="peer-address">
- <properties>
- <help>IP address of failover peer [REQUIRED]</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address of failover peer</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="status">
- <properties>
- <help>DHCP failover peer status (primary|secondary) [REQUIRED]</help>
- <completionHelp>
- <list>primary secondary</list>
- </completionHelp>
- <constraint>
- <regex>^(primary|secondary)$</regex>
- </constraint>
- <constraintErrorMessage>Invalid DHCP failover peer status</constraintErrorMessage>
- </properties>
- </leafNode>
- </children>
- </node>
<leafNode name="ip-forwarding">
<properties>
<help>Enable IP forwarding on client</help>
@@ -225,19 +210,8 @@
</properties>
<defaultValue>86400</defaultValue>
</leafNode>
- <leafNode name="ntp-server">
- <properties>
- <help>IP address of NTP server</help>
- <valueHelp>
- <format>ipv4</format>
- <description>NTP server IPv4 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/dhcp/ntp-server.xml.i>
+ #include <include/dhcp/ping-check.xml.i>
<leafNode name="pop-server">
<properties>
<help>IP address of POP3 server</help>
@@ -280,9 +254,9 @@
<properties>
<help>DHCP lease range</help>
<constraint>
- <regex>[-_a-zA-Z0-9.]+</regex>
+ <regex>^[-_a-zA-Z0-9.]+$</regex>
</constraint>
- <constraintErrorMessage>Invalid DHCP lease range name. May only contain letters, numbers and .-_</constraintErrorMessage>
+ <constraintErrorMessage>Invalid range name, may only be alphanumeric, dot and hyphen</constraintErrorMessage>
</properties>
<children>
<leafNode name="start">
@@ -315,9 +289,9 @@
<properties>
<help>Name of static mapping</help>
<constraint>
- <regex>[-_a-zA-Z0-9.]+</regex>
+ <regex>^[-_a-zA-Z0-9.]+$</regex>
</constraint>
- <constraintErrorMessage>Invalid static mapping name. May only contain letters, numbers and .-_</constraintErrorMessage>
+ <constraintErrorMessage>Invalid static mapping name, may only be alphanumeric, dot and hyphen</constraintErrorMessage>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
@@ -357,26 +331,21 @@
</leafNode>
</children>
</tagNode>
- <node name="static-route">
+ <tagNode name="static-route">
<properties>
- <help>Classless static route</help>
+ <help>Classless static route destination subnet [REQUIRED]</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
</properties>
<children>
- <leafNode name="destination-subnet">
- <properties>
- <help>Destination subnet [REQUIRED]</help>
- <valueHelp>
- <format>ipv4net</format>
- <description>IPv4 address and prefix length</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-prefix"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="router">
+ <leafNode name="next-hop">
<properties>
- <help>IP address of router to be used to reach the destination subnet [REQUIRED]</help>
+ <help>IP address of router to be used to reach the destination subnet</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address of router</description>
@@ -387,7 +356,7 @@
</properties>
</leafNode>
</children>
- </node>
+ </tagNode >
<leafNode name="subnet-parameters">
<properties>
<help>Additional subnet parameters for DHCP server. You must
diff --git a/interface-definitions/dhcpv6-relay.xml.in b/interface-definitions/dhcpv6-relay.xml.in
index 308f94a01..7162cf353 100644
--- a/interface-definitions/dhcpv6-relay.xml.in
+++ b/interface-definitions/dhcpv6-relay.xml.in
@@ -35,7 +35,7 @@
<properties>
<help>Maximum hop count for which requests will be processed</help>
<valueHelp>
- <format>1-255</format>
+ <format>u32:1-255</format>
<description>Hop count (default: 10)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in
index 5d6c64685..fb96571f5 100644
--- a/interface-definitions/dhcpv6-server.xml.in
+++ b/interface-definitions/dhcpv6-server.xml.in
@@ -1,5 +1,4 @@
<?xml version="1.0"?>
-<!-- DHCPv6 server configuration -->
<interfaceDefinition>
<node name="service">
<children>
@@ -15,26 +14,14 @@
<help>Additional global parameters for DHCPv6 server</help>
</properties>
<children>
- <leafNode name="name-server">
- <properties>
- <help>IPv6 address of a Recursive DNS Server</help>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of DNS name server</description>
- </valueHelp>
- <constraint>
- <validator name="ipv6-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/name-server-ipv6.xml.i>
</children>
</node>
<leafNode name="preference">
<properties>
<help>Preference of this DHCPv6 server compared with others</help>
<valueHelp>
- <format>0-255</format>
+ <format>u32:0-255</format>
<description>DHCPv6 server preference (0-255)</description>
</valueHelp>
<constraint>
@@ -53,6 +40,7 @@
</properties>
<children>
#include <include/generic-disable-node.xml.i>
+ #include <include/generic-description.xml.i>
<node name="common-options">
<properties>
<help>Common options to distribute to all clients, including stateless clients</help>
@@ -62,7 +50,7 @@
<properties>
<help>Time (in seconds) that stateless clients should wait between refreshing the information they were given</help>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>DHCPv6 information refresh time</description>
</valueHelp>
<constraint>
@@ -70,20 +58,8 @@
</constraint>
</properties>
</leafNode>
- #include <include/dhcp-server-domain-search.xml.i>
- <leafNode name="name-server">
- <properties>
- <help>IPv6 address of a Recursive DNS Server</help>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of DNS name server</description>
- </valueHelp>
- <constraint>
- <validator name="ipv6-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/dhcp/domain-search.xml.i>
+ #include <include/name-server-ipv6.xml.i>
</children>
</node>
<tagNode name="subnet">
@@ -151,7 +127,7 @@
</tagNode>
</children>
</node>
- #include <include/dhcp-server-domain-search.xml.i>
+ #include <include/dhcp/domain-search.xml.i>
<node name="lease-time">
<properties>
<help>Parameters relating to the lease time</help>
@@ -161,7 +137,7 @@
<properties>
<help>Default time (in seconds) that will be assigned to a lease</help>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>DHCPv6 valid lifetime</description>
</valueHelp>
<constraint>
@@ -173,7 +149,7 @@
<properties>
<help>Maximum time (in seconds) that will be assigned to a lease</help>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>Maximum lease time in seconds</description>
</valueHelp>
<constraint>
@@ -185,7 +161,7 @@
<properties>
<help>Minimum time (in seconds) that will be assigned to a lease</help>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>Minimum lease time in seconds</description>
</valueHelp>
<constraint>
@@ -195,19 +171,7 @@
</leafNode>
</children>
</node>
- <leafNode name="name-server">
- <properties>
- <help>IPv6 address of a Recursive DNS Server</help>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of DNS name server</description>
- </valueHelp>
- <constraint>
- <validator name="ipv6-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/name-server-ipv6.xml.i>
<leafNode name="nis-domain">
<properties>
<help>NIS domain name for client to use</help>
@@ -273,7 +237,7 @@
<properties>
<help>Length in bits of prefixes to be delegated</help>
<valueHelp>
- <format>32-64</format>
+ <format>u32:32-64</format>
<description>Delagated prefix length (32-64)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in
index ff632e1d1..2b1644609 100644
--- a/interface-definitions/dns-domain-name.xml.in
+++ b/interface-definitions/dns-domain-name.xml.in
@@ -1,37 +1,34 @@
<?xml version="1.0"?>
-<!-- host-name configuration -->
<interfaceDefinition>
<node name="system">
<children>
<leafNode name="name-server" owner="${vyos_conf_scripts_dir}/host_name.py">
<properties>
- <help>Domain Name Servers (DNS) used by the system (resolv.conf)</help>
+ <help>System Domain Name Servers (DNS)</help>
<priority>400</priority>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
<valueHelp>
<format>ipv4</format>
- <description>Domain Name Server (DNS) address</description>
+ <description>Domain Name Server IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
- <description>Domain Name Server (DNS) address</description>
+ <description>Domain Name Server IPv6 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Use Domain Name Server from DHCP interface</description>
</valueHelp>
<multi/>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
+ <validator name="interface-name"/>
</constraint>
</properties>
</leafNode>
- <leafNode name="name-servers-dhcp" owner="${vyos_conf_scripts_dir}/host_name.py">
- <properties>
- <help>Interfaces whose DHCP client nameservers will be used by the system (resolv.conf)</help>
- <priority>400</priority>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- <multi/>
- </properties>
- </leafNode>
<leafNode name="host-name" owner="${vyos_conf_scripts_dir}/host_name.py">
<properties>
<help>System host name (default: vyos)</help>
diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in
index b0b9158c8..250642691 100644
--- a/interface-definitions/dns-dynamic.xml.in
+++ b/interface-definitions/dns-dynamic.xml.in
@@ -49,7 +49,7 @@
<properties>
<help>Time To Live (default: 600)</help>
<valueHelp>
- <format>1-86400</format>
+ <format>u32:1-86400</format>
<description>DNS forwarding cache size</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 66b4db403..5b0c87597 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -18,11 +18,11 @@
<properties>
<help>DNS forwarding cache size (default: 10000)</help>
<valueHelp>
- <format>0-10000</format>
+ <format>u32:0-2147483647</format>
<description>DNS forwarding cache size</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-10000"/>
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
<defaultValue>10000</defaultValue>
@@ -111,6 +111,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="no-serve-rfc1918">
+ <properties>
+ <help>Makes the server authoritatively not aware of RFC1918 addresses</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="allow-from">
<properties>
<help>Networks allowed to query this server</help>
@@ -133,7 +139,7 @@
<properties>
<help>Maximum amount of time negative entries are cached (default: 3600)</help>
<valueHelp>
- <format>0-7200</format>
+ <format>u32:0-7200</format>
<description>Seconds to cache NXDOMAIN entries</description>
</valueHelp>
<constraint>
@@ -142,24 +148,7 @@
</properties>
<defaultValue>3600</defaultValue>
</leafNode>
- <leafNode name="name-server">
- <properties>
- <help>Domain Name Servers (DNS) addresses [OPTIONAL]</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Domain Name Server (DNS) IPv4 address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>Domain Name Server (DNS) IPv6 address</description>
- </valueHelp>
- <multi/>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/name-server-ipv4-ipv6.xml.i>
<leafNode name="source-address">
<properties>
<help>Local addresses from which to send DNS queries</help>
diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in
index b3980d9e2..b0f308afd 100644
--- a/interface-definitions/flow-accounting-conf.xml.in
+++ b/interface-definitions/flow-accounting-conf.xml.in
@@ -267,7 +267,7 @@
<properties>
<help>Expiry scan interval</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>Expiry scan interval (default 60)</description>
</valueHelp>
<constraint>
@@ -279,7 +279,7 @@
<properties>
<help>Generic flow timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>Generic flow timeout in seconds (default 3600)</description>
</valueHelp>
<constraint>
@@ -291,7 +291,7 @@
<properties>
<help>ICMP timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>ICMP timeout in seconds (default 300)</description>
</valueHelp>
<constraint>
@@ -303,7 +303,7 @@
<properties>
<help>Max active timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>Max active timeout in seconds (default 604800)</description>
</valueHelp>
<constraint>
@@ -315,7 +315,7 @@
<properties>
<help>TCP finish timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>TCP FIN timeout in seconds (default 300)</description>
</valueHelp>
<constraint>
@@ -327,7 +327,7 @@
<properties>
<help>TCP generic timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>TCP generic timeout in seconds (default 3600)</description>
</valueHelp>
<constraint>
@@ -339,7 +339,7 @@
<properties>
<help>TCP reset timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>TCP RST timeout in seconds (default 120)</description>
</valueHelp>
<constraint>
@@ -351,7 +351,7 @@
<properties>
<help>UDP timeout value</help>
<valueHelp>
- <format>0-2147483647</format>
+ <format>u32:0-2147483647</format>
<description>UDP timeout in seconds (default 300)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in
index b613e30c1..ccb77910a 100644
--- a/interface-definitions/https.xml.in
+++ b/interface-definitions/https.xml.in
@@ -1,7 +1,5 @@
<?xml version="1.0"?>
-<!-- HTTPS configuration -->
<interfaceDefinition>
- <syntaxVersion component='https' version='2'></syntaxVersion>
<node name="service">
<children>
<node name="https" owner="${vyos_conf_scripts_dir}/https.py">
@@ -48,7 +46,7 @@
<properties>
<help>Port to listen for HTTPS requests; default 443</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
@@ -151,9 +149,9 @@
</properties>
</leafNode>
<leafNode name="email">
- <properties>
- <help>Email address to associate with certificate</help>
- </properties>
+ <properties>
+ <help>Email address to associate with certificate</help>
+ </properties>
</leafNode>
</children>
</node>
diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in
index d0f44eada..91c912d8b 100644
--- a/interface-definitions/igmp-proxy.xml.in
+++ b/interface-definitions/igmp-proxy.xml.in
@@ -65,7 +65,7 @@
<properties>
<help>TTL threshold (default: 1)</help>
<valueHelp>
- <format>1-255</format>
+ <format>u32:1-255</format>
<description>TTL threshold for the interfaces (default: 1)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/accel-ppp/auth-local-users.xml.i b/interface-definitions/include/accel-ppp/auth-local-users.xml.i
index 308d6510d..1b40a9ea7 100644
--- a/interface-definitions/include/accel-ppp/auth-local-users.xml.i
+++ b/interface-definitions/include/accel-ppp/auth-local-users.xml.i
@@ -18,6 +18,9 @@
<leafNode name="static-ip">
<properties>
<help>Static client IP address</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
</properties>
<defaultValue>*</defaultValue>
</leafNode>
diff --git a/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
index bd3dadf8d..a692f2335 100644
--- a/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
+++ b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
@@ -27,6 +27,7 @@
<validator name="numeric" argument="--range 48-128"/>
</constraint>
</properties>
+ <defaultValue>64</defaultValue>
</leafNode>
</children>
</tagNode>
diff --git a/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i b/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i
index 9f223d7ed..019601c85 100644
--- a/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i
+++ b/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>PPP interface cache</help>
<valueHelp>
- <format>1-256000</format>
+ <format>u32:1-256000</format>
<description>Count of interfaces to keep in cache</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/accel-ppp/ppp-options-ipv4.xml.i b/interface-definitions/include/accel-ppp/ppp-options-ipv4.xml.i
new file mode 100644
index 000000000..3e065329d
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/ppp-options-ipv4.xml.i
@@ -0,0 +1,23 @@
+<!-- include start from accel-ppp/ppp-options-ipv4.xml.i -->
+<leafNode name="ipv4">
+ <properties>
+ <help>IPv4 negotiation algorithm</help>
+ <constraint>
+ <regex>^(deny|allow)$</regex>
+ </constraint>
+ <constraintErrorMessage>invalid value</constraintErrorMessage>
+ <valueHelp>
+ <format>deny</format>
+ <description>Do not negotiate IPv4</description>
+ </valueHelp>
+ <valueHelp>
+ <format>allow</format>
+ <description>Negotiate IPv4 only if client requests</description>
+ </valueHelp>
+ <completionHelp>
+ <list>deny allow</list>
+ </completionHelp>
+ </properties>
+ <defaultValue>allow</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/accel-ppp/ppp-options-ipv6.xml.i b/interface-definitions/include/accel-ppp/ppp-options-ipv6.xml.i
new file mode 100644
index 000000000..b9fbac5c6
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/ppp-options-ipv6.xml.i
@@ -0,0 +1,31 @@
+<!-- include start from accel-ppp/ppp-options-ipv6.xml.i -->
+<leafNode name="ipv6">
+ <properties>
+ <help>IPv6 (IPCP6) negotiation algorithm</help>
+ <constraint>
+ <regex>^(deny|allow|prefer|require)$</regex>
+ </constraint>
+ <constraintErrorMessage>invalid value</constraintErrorMessage>
+ <valueHelp>
+ <format>deny</format>
+ <description>Do not negotiate IPv6</description>
+ </valueHelp>
+ <valueHelp>
+ <format>allow</format>
+ <description>Negotiate IPv6 only if client requests</description>
+ </valueHelp>
+ <valueHelp>
+ <format>prefer</format>
+ <description>Ask client for IPv6 negotiation, do not fail if it rejects</description>
+ </valueHelp>
+ <valueHelp>
+ <format>require</format>
+ <description>Require IPv6 negotiation</description>
+ </valueHelp>
+ <completionHelp>
+ <list>deny allow prefer require</list>
+ </completionHelp>
+ </properties>
+ <defaultValue>deny</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i
index e65088c43..44ec64d7f 100644
--- a/interface-definitions/include/accel-ppp/radius-additions.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i
@@ -5,7 +5,7 @@
<properties>
<help>Maximum jitter value in seconds to be applied to accounting information interval</help>
<valueHelp>
- <format>1-60</format>
+ <format>u32:1-60</format>
<description>Maximum jitter value in seconds</description>
</valueHelp>
<constraint>
@@ -20,7 +20,7 @@
<properties>
<help>Accounting port</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Numeric IP port (default: 1813)</description>
</valueHelp>
<constraint>
@@ -34,7 +34,7 @@
<properties>
<help>Mark server unavailable for &lt;n&gt; seconds on failure</help>
<valueHelp>
- <format>0-600</format>
+ <format>u32:0-600</format>
<description>Fail time penalty</description>
</valueHelp>
<constraint>
@@ -50,7 +50,7 @@
<properties>
<help>Timeout in seconds to wait response from RADIUS server</help>
<valueHelp>
- <format>1-60</format>
+ <format>u32:1-60</format>
<description>Timeout in seconds</description>
</valueHelp>
<constraint>
@@ -64,7 +64,7 @@
<properties>
<help>Timeout for Interim-Update packets, terminate session afterwards (default 3 seconds)</help>
<valueHelp>
- <format>0-60</format>
+ <format>u32:0-60</format>
<description>Timeout in seconds, 0 to keep active</description>
</valueHelp>
<constraint>
@@ -78,7 +78,7 @@
<properties>
<help>Number of tries to send Access-Request/Accounting-Request queries</help>
<valueHelp>
- <format>1-20</format>
+ <format>u32:1-20</format>
<description>Maximum tries</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/bfd.xml.i b/interface-definitions/include/bfd.xml.i
new file mode 100644
index 000000000..2bc3664e1
--- /dev/null
+++ b/interface-definitions/include/bfd.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from bfd.xml.i -->
+<leafNode name="bfd">
+ <properties>
+ <help>Enable Bidirectional Forwarding Detection (BFD)</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/dhcp/domain-name.xml.i b/interface-definitions/include/dhcp/domain-name.xml.i
new file mode 100644
index 000000000..410e27d29
--- /dev/null
+++ b/interface-definitions/include/dhcp/domain-name.xml.i
@@ -0,0 +1,11 @@
+<!-- include start from dhcp/domain-name.xml.i -->
+<leafNode name="domain-name">
+ <properties>
+ <help>Client Domain Name</help>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ <constraintErrorMessage>Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/dhcp-server-domain-search.xml.i b/interface-definitions/include/dhcp/domain-search.xml.i
index 4fc55097b..bcc8fcd12 100644
--- a/interface-definitions/include/dhcp-server-domain-search.xml.i
+++ b/interface-definitions/include/dhcp/domain-search.xml.i
@@ -1,4 +1,4 @@
-<!-- include start from dhcp-server-domain-search.xml.i -->
+<!-- include start from dhcp/domain-search.xml.i -->
<leafNode name="domain-search">
<properties>
<help>Client Domain Name search list</help>
diff --git a/interface-definitions/include/dhcp/ntp-server.xml.i b/interface-definitions/include/dhcp/ntp-server.xml.i
new file mode 100644
index 000000000..32d8207e5
--- /dev/null
+++ b/interface-definitions/include/dhcp/ntp-server.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from dhcp/ntp-server.xml.i -->
+ <leafNode name="ntp-server">
+ <properties>
+ <help>IP address of NTP server</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>NTP server IPv4 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/dhcp/ping-check.xml.i b/interface-definitions/include/dhcp/ping-check.xml.i
new file mode 100644
index 000000000..a506f68e4
--- /dev/null
+++ b/interface-definitions/include/dhcp/ping-check.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from dhcp/ping-check.xml.i -->
+<leafNode name="ping-check">
+ <properties>
+ <help>Sends ICMP Echo request to the address being assigned</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/generic-description.xml.i b/interface-definitions/include/generic-description.xml.i
new file mode 100644
index 000000000..03fc564e6
--- /dev/null
+++ b/interface-definitions/include/generic-description.xml.i
@@ -0,0 +1,11 @@
+<!-- include start from generic-description.xml.i -->
+<leafNode name="description">
+ <properties>
+ <help>Description</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Description</description>
+ </valueHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i b/interface-definitions/include/interface/arp-cache-timeout.xml.i
index b269fecd8..70e69e14a 100644
--- a/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i
+++ b/interface-definitions/include/interface/arp-cache-timeout.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>ARP cache entry timeout in seconds</help>
<valueHelp>
- <format>1-86400</format>
+ <format>u32:1-86400</format>
<description>ARP cache entry timout in seconds (default 30)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-description.xml.i b/interface-definitions/include/interface/description.xml.i
index d618b50d2..d618b50d2 100644
--- a/interface-definitions/include/interface/interface-description.xml.i
+++ b/interface-definitions/include/interface/description.xml.i
diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i
index a569659a4..a0cac34f1 100644
--- a/interface-definitions/include/interface/dhcpv6-options.xml.i
+++ b/interface-definitions/include/interface/dhcpv6-options.xml.i
@@ -38,7 +38,7 @@
<properties>
<help>Request IPv6 prefix length from peer</help>
<valueHelp>
- <format>32-64</format>
+ <format>u32:32-64</format>
<description>Length of delegated prefix</description>
</valueHelp>
<constraint>
@@ -71,7 +71,7 @@
<properties>
<help>Interface site-Level aggregator (SLA)</help>
<valueHelp>
- <format>0-128</format>
+ <format>u32:0-128</format>
<description>Decimal integer which fits in the length of SLA IDs</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-dial-on-demand.xml.i b/interface-definitions/include/interface/dial-on-demand.xml.i
index 66edd9678..66edd9678 100644
--- a/interface-definitions/include/interface/interface-dial-on-demand.xml.i
+++ b/interface-definitions/include/interface/dial-on-demand.xml.i
diff --git a/interface-definitions/include/interface/interface-disable-arp-filter.xml.i b/interface-definitions/include/interface/disable-arp-filter.xml.i
index 49cddaf76..49cddaf76 100644
--- a/interface-definitions/include/interface/interface-disable-arp-filter.xml.i
+++ b/interface-definitions/include/interface/disable-arp-filter.xml.i
diff --git a/interface-definitions/include/interface/interface-disable-forwarding.xml.i b/interface-definitions/include/interface/disable-forwarding.xml.i
index cb6ef0475..cee9d2a8d 100644
--- a/interface-definitions/include/interface/interface-disable-forwarding.xml.i
+++ b/interface-definitions/include/interface/disable-forwarding.xml.i
@@ -1,7 +1,7 @@
<!-- include start from interface/interface-disable-forwarding.xml.i -->
<leafNode name="disable-forwarding">
<properties>
- <help>Disable IPv4 forwarding on this interface</help>
+ <help>Disable IP forwarding on this interface</help>
<valueless/>
</properties>
</leafNode>
diff --git a/interface-definitions/include/interface/interface-disable-link-detect.xml.i b/interface-definitions/include/interface/disable-link-detect.xml.i
index c528885b2..c528885b2 100644
--- a/interface-definitions/include/interface/interface-disable-link-detect.xml.i
+++ b/interface-definitions/include/interface/disable-link-detect.xml.i
diff --git a/interface-definitions/include/interface/interface-disable.xml.i b/interface-definitions/include/interface/disable.xml.i
index d90e6395b..d90e6395b 100644
--- a/interface-definitions/include/interface/interface-disable.xml.i
+++ b/interface-definitions/include/interface/disable.xml.i
diff --git a/interface-definitions/include/interface/interface-eapol.xml.i b/interface-definitions/include/interface/eapol.xml.i
index 92b7a3f35..92b7a3f35 100644
--- a/interface-definitions/include/interface/interface-eapol.xml.i
+++ b/interface-definitions/include/interface/eapol.xml.i
diff --git a/interface-definitions/include/interface/interface-enable-arp-accept.xml.i b/interface-definitions/include/interface/enable-arp-accept.xml.i
index 7c5d51857..7c5d51857 100644
--- a/interface-definitions/include/interface/interface-enable-arp-accept.xml.i
+++ b/interface-definitions/include/interface/enable-arp-accept.xml.i
diff --git a/interface-definitions/include/interface/interface-enable-arp-announce.xml.i b/interface-definitions/include/interface/enable-arp-announce.xml.i
index f44599c54..f44599c54 100644
--- a/interface-definitions/include/interface/interface-enable-arp-announce.xml.i
+++ b/interface-definitions/include/interface/enable-arp-announce.xml.i
diff --git a/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i b/interface-definitions/include/interface/enable-arp-ignore.xml.i
index 3ea39613c..3ea39613c 100644
--- a/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i
+++ b/interface-definitions/include/interface/enable-arp-ignore.xml.i
diff --git a/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i b/interface-definitions/include/interface/enable-proxy-arp.xml.i
index dbdeeb7a7..dbdeeb7a7 100644
--- a/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i
+++ b/interface-definitions/include/interface/enable-proxy-arp.xml.i
diff --git a/interface-definitions/include/interface/interface-hw-id.xml.i b/interface-definitions/include/interface/hw-id.xml.i
index 989cd9cb7..989cd9cb7 100644
--- a/interface-definitions/include/interface/interface-hw-id.xml.i
+++ b/interface-definitions/include/interface/hw-id.xml.i
diff --git a/interface-definitions/include/interface/interface-ipv4-options.xml.i b/interface-definitions/include/interface/interface-ipv4-options.xml.i
deleted file mode 100644
index c2d0677b7..000000000
--- a/interface-definitions/include/interface/interface-ipv4-options.xml.i
+++ /dev/null
@@ -1,18 +0,0 @@
-<!-- include start from interface/interface-ipv4-options.xml.i -->
-<node name="ip">
- <properties>
- <help>IPv4 routing parameters</help>
- </properties>
- <children>
- #include <include/interface/interface-arp-cache-timeout.xml.i>
- #include <include/interface/interface-disable-arp-filter.xml.i>
- #include <include/interface/interface-disable-forwarding.xml.i>
- #include <include/interface/interface-enable-arp-accept.xml.i>
- #include <include/interface/interface-enable-arp-announce.xml.i>
- #include <include/interface/interface-enable-arp-ignore.xml.i>
- #include <include/interface/interface-enable-proxy-arp.xml.i>
- #include <include/interface/interface-proxy-arp-pvlan.xml.i>
- #include <include/interface/interface-source-validation.xml.i>
- </children>
-</node>
-<!-- include end -->
diff --git a/interface-definitions/include/interface/ipv4-options.xml.i b/interface-definitions/include/interface/ipv4-options.xml.i
new file mode 100644
index 000000000..5a45487c5
--- /dev/null
+++ b/interface-definitions/include/interface/ipv4-options.xml.i
@@ -0,0 +1,18 @@
+<!-- include start from interface/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>
+ #include <include/interface/source-validation.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i b/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i
deleted file mode 100644
index 4adb77d1b..000000000
--- a/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- include start from interface/ipv6-disable-forwarding.xml.i -->
-<leafNode name="disable-forwarding">
- <properties>
- <help>Disable IPv6 forwarding on this interface</help>
- <valueless/>
- </properties>
-</leafNode>
-<!-- include end -->
diff --git a/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i
index 2b5ec0281..babe6d20f 100644
--- a/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i
+++ b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i
@@ -3,12 +3,12 @@
<properties>
<help>Number of NS messages to send while performing DAD (default: 1)</help>
<valueHelp>
- <format>1-n</format>
- <description>Number of NS messages to send while performing DAD</description>
+ <format>u32:0</format>
+ <description>Disable Duplicate Address Dectection (DAD)</description>
</valueHelp>
<valueHelp>
- <format>0</format>
- <description>Disable Duplicate Address Dectection (DAD)</description>
+ <format>u32:1-n</format>
+ <description>Number of NS messages to send while performing DAD</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--non-negative"/>
diff --git a/interface-definitions/include/interface/interface-ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i
index dcd5a8710..a5b40c789 100644
--- a/interface-definitions/include/interface/interface-ipv6-options.xml.i
+++ b/interface-definitions/include/interface/ipv6-options.xml.i
@@ -4,8 +4,8 @@
<help>IPv6 routing parameters</help>
</properties>
<children>
+ #include <include/interface/disable-forwarding.xml.i>
#include <include/interface/ipv6-address.xml.i>
- #include <include/interface/ipv6-disable-forwarding.xml.i>
#include <include/interface/ipv6-dup-addr-detect-transmits.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/interface/interface-mac.xml.i b/interface-definitions/include/interface/mac.xml.i
index d7107ad23..d7107ad23 100644
--- a/interface-definitions/include/interface/interface-mac.xml.i
+++ b/interface-definitions/include/interface/mac.xml.i
diff --git a/interface-definitions/include/interface/interface-mirror.xml.i b/interface-definitions/include/interface/mirror.xml.i
index b3b45fb43..b3b45fb43 100644
--- a/interface-definitions/include/interface/interface-mirror.xml.i
+++ b/interface-definitions/include/interface/mirror.xml.i
diff --git a/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i b/interface-definitions/include/interface/mtu-1200-16000.xml.i
index 3241ba912..730c6e00d 100644
--- a/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i
+++ b/interface-definitions/include/interface/mtu-1200-16000.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
- <format>1200-16000</format>
+ <format>u32:1200-16000</format>
<description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i b/interface-definitions/include/interface/mtu-1450-16000.xml.i
index 0a35bbbaa..96cfa7054 100644
--- a/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i
+++ b/interface-definitions/include/interface/mtu-1450-16000.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
- <format>1450-16000</format>
+ <format>u32:1450-16000</format>
<description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-mtu-64-8024.xml.i b/interface-definitions/include/interface/mtu-64-8024.xml.i
index f75de02ba..3719ece24 100644
--- a/interface-definitions/include/interface/interface-mtu-64-8024.xml.i
+++ b/interface-definitions/include/interface/mtu-64-8024.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
- <format>64-8024</format>
+ <format>u32:64-8024</format>
<description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-mtu-68-1500.xml.i b/interface-definitions/include/interface/mtu-68-1500.xml.i
index 9e6fe8760..d74cdfa9c 100644
--- a/interface-definitions/include/interface/interface-mtu-68-1500.xml.i
+++ b/interface-definitions/include/interface/mtu-68-1500.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
- <format>68-1500</format>
+ <format>u32:68-1500</format>
<description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-mtu-68-16000.xml.i b/interface-definitions/include/interface/mtu-68-16000.xml.i
index 83af7bbd4..41340fbf3 100644
--- a/interface-definitions/include/interface/interface-mtu-68-16000.xml.i
+++ b/interface-definitions/include/interface/mtu-68-16000.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
- <format>68-16000</format>
+ <format>u32:68-16000</format>
<description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i b/interface-definitions/include/interface/parameters-flowlabel.xml.i
index f5e868a64..a89bb13f1 100644
--- a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i
+++ b/interface-definitions/include/interface/parameters-flowlabel.xml.i
@@ -2,9 +2,16 @@
<leafNode name="flowlabel">
<properties>
<help>Specifies the flow label to use in outgoing packets</help>
+ <completionHelp>
+ <list>inherit</list>
+ </completionHelp>
<valueHelp>
- <format>0x0-0x0FFFFF</format>
- <description>Tunnel key, 'inherit' or hex value</description>
+ <format>inherit</format>
+ <description>Copy field from original header</description>
+ </valueHelp>
+ <valueHelp>
+ <format>0x0-0x0fffff</format>
+ <description>Tunnel key, or hex value</description>
</valueHelp>
<constraint>
<regex>^((0x){0,1}(0?[0-9A-Fa-f]{1,5})|inherit)$</regex>
diff --git a/interface-definitions/include/interface/interface-parameters-key.xml.i b/interface-definitions/include/interface/parameters-key.xml.i
index 1b1d67174..1b1d67174 100644
--- a/interface-definitions/include/interface/interface-parameters-key.xml.i
+++ b/interface-definitions/include/interface/parameters-key.xml.i
diff --git a/interface-definitions/include/interface/interface-parameters-tos.xml.i b/interface-definitions/include/interface/parameters-tos.xml.i
index 83b4e0671..1b342a43e 100644
--- a/interface-definitions/include/interface/interface-parameters-tos.xml.i
+++ b/interface-definitions/include/interface/parameters-tos.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Specifies TOS value to use in outgoing packets</help>
<valueHelp>
- <format>0-99</format>
+ <format>u32:0-99</format>
<description>Type of Service (TOS)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-parameters-ttl.xml.i b/interface-definitions/include/interface/parameters-ttl.xml.i
index 21a5e5cd9..8ef8c9149 100644
--- a/interface-definitions/include/interface/interface-parameters-ttl.xml.i
+++ b/interface-definitions/include/interface/parameters-ttl.xml.i
@@ -3,11 +3,11 @@
<properties>
<help>Specifies TTL value to use in outgoing packets</help>
<valueHelp>
- <format>0</format>
- <description>Copy value from original IP header</description>
+ <format>u32:0</format>
+ <description>Inherit - copy value from original IP header</description>
</valueHelp>
<valueHelp>
- <format>1-255</format>
+ <format>u32:1-255</format>
<description>Time to Live</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i b/interface-definitions/include/interface/proxy-arp-pvlan.xml.i
index 153dfc072..153dfc072 100644
--- a/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i
+++ b/interface-definitions/include/interface/proxy-arp-pvlan.xml.i
diff --git a/interface-definitions/include/interface/interface-source-validation.xml.i b/interface-definitions/include/interface/source-validation.xml.i
index 70914f2e9..70914f2e9 100644
--- a/interface-definitions/include/interface/interface-source-validation.xml.i
+++ b/interface-definitions/include/interface/source-validation.xml.i
diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i
index 85885c153..7a41bb242 100644
--- a/interface-definitions/include/interface/vif-s.xml.i
+++ b/interface-definitions/include/interface/vif-s.xml.i
@@ -2,6 +2,10 @@
<tagNode name="vif-s">
<properties>
<help>QinQ TAG-S Virtual Local Area Network (VLAN) ID</help>
+ <valueHelp>
+ <format>u32:0-4094</format>
+ <description>QinQ Virtual Local Area Network (VLAN) ID</description>
+ </valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4094"/>
</constraint>
@@ -9,11 +13,11 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
<leafNode name="protocol">
<properties>
<help>Protocol used for service VLAN (default: 802.1ad)</help>
@@ -35,10 +39,10 @@
</properties>
<defaultValue>802.1ad</defaultValue>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #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-16000.xml.i>
<tagNode name="vif-c">
<properties>
<help>QinQ TAG-C Virtual Local Area Network (VLAN) ID</help>
@@ -49,19 +53,19 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
+ #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-16000.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
<!-- include end -->
diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i
index 4e7aeb0f9..fdf09c5f9 100644
--- a/interface-definitions/include/interface/vif.xml.i
+++ b/interface-definitions/include/interface/vif.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Virtual Local Area Network (VLAN) ID</help>
<valueHelp>
- <format>0-4094</format>
+ <format>u32:0-4094</format>
<description>Virtual Local Area Network (VLAN) ID</description>
</valueHelp>
<constraint>
@@ -13,12 +13,12 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
<leafNode name="egress-qos">
<properties>
<help>VLAN egress QoS</help>
@@ -43,10 +43,10 @@
<constraintErrorMessage>QoS mapping should be in the format of '0:7 2:3' with numbers 0-9</constraintErrorMessage>
</properties>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #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-16000.xml.i>
</children>
</tagNode>
<!-- include end -->
diff --git a/interface-definitions/include/interface/interface-vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i
index ef6ca1241..ef6ca1241 100644
--- a/interface-definitions/include/interface/interface-vrf.xml.i
+++ b/interface-definitions/include/interface/vrf.xml.i
diff --git a/interface-definitions/include/isis-redistribute-ipv4.xml.i b/interface-definitions/include/isis-redistribute-ipv4.xml.i
deleted file mode 100644
index 774086a81..000000000
--- a/interface-definitions/include/isis-redistribute-ipv4.xml.i
+++ /dev/null
@@ -1,56 +0,0 @@
-<!-- include start from isis-redistribute-ipv4.xml.i -->
-<node name="level-1">
- <properties>
- <help>Redistribute into level-1</help>
- </properties>
- <children>
- <leafNode name="metric">
- <properties>
- <help>Metric for redistributed routes</help>
- <valueHelp>
- <format>u32:0-16777215</format>
- <description>ISIS default metric</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-16777215"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="route-map">
- <properties>
- <help>Route map reference</help>
- <completionHelp>
- <path>policy route-map</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
-</node>
-<node name="level-2">
- <properties>
- <help>Redistribute into level-2</help>
- </properties>
- <children>
- <leafNode name="metric">
- <properties>
- <help>Metric for redistributed routes</help>
- <valueHelp>
- <format>u32:0-16777215</format>
- <description>ISIS default metric</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-16777215"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="route-map">
- <properties>
- <help>Route map reference</help>
- <completionHelp>
- <path>policy route-map</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
-</node>
-<!-- include end -->
diff --git a/interface-definitions/include/isis/default-information-level.xml.i b/interface-definitions/include/isis/default-information-level.xml.i
new file mode 100644
index 000000000..5ade72a4b
--- /dev/null
+++ b/interface-definitions/include/isis/default-information-level.xml.i
@@ -0,0 +1,32 @@
+<!-- include start from isis/default-information-level.xml.i -->
+<node name="level-1">
+ <properties>
+ <help>Distribute default route into level-1</help>
+ </properties>
+ <children>
+ <leafNode name="always">
+ <properties>
+ <help>Always advertise default route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/isis/metric.xml.i>
+ #include <include/route-map.xml.i>
+ </children>
+</node>
+<node name="level-2">
+ <properties>
+ <help>Distribute default route into level-2</help>
+ </properties>
+ <children>
+ <leafNode name="always">
+ <properties>
+ <help>Always advertise default route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/isis/metric.xml.i>
+ #include <include/route-map.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/metric.xml.i b/interface-definitions/include/isis/metric.xml.i
new file mode 100644
index 000000000..30e2cdc10
--- /dev/null
+++ b/interface-definitions/include/isis/metric.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from isis/metric.xml.i -->
+<leafNode name="metric">
+ <properties>
+ <help>Set default metric for circuit</help>
+ <valueHelp>
+ <format>u32:0-16777215</format>
+ <description>Default metric value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777215"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/passive.xml.i b/interface-definitions/include/isis/passive.xml.i
new file mode 100644
index 000000000..6d05f8cc7
--- /dev/null
+++ b/interface-definitions/include/isis/passive.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from isis/passive.xml.i -->
+<leafNode name="passive">
+ <properties>
+ <help>Configure passive mode for interface</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i
new file mode 100644
index 000000000..84e2f7bb2
--- /dev/null
+++ b/interface-definitions/include/isis/protocol-common-config.xml.i
@@ -0,0 +1,769 @@
+<!-- include start from isis/protocol-common-config.xml.i -->
+<node name="area-password">
+ <properties>
+ <help>Configure the authentication password for an area</help>
+ </properties>
+ <children>
+ <leafNode name="plaintext-password">
+ <properties>
+ <help>Plain-text authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Level-wide password</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="md5">
+ <properties>
+ <help>MD5 authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Level-wide password</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="default-information">
+ <properties>
+ <help>Control distribution of default information</help>
+ </properties>
+ <children>
+ <node name="originate">
+ <properties>
+ <help>Distribute a default route</help>
+ </properties>
+ <children>
+ <node name="ipv4">
+ <properties>
+ <help>Distribute default route for IPv4</help>
+ </properties>
+ <children>
+ #include <include/isis/default-information-level.xml.i>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>Distribute default route for IPv6</help>
+ </properties>
+ <children>
+ #include <include/isis/default-information-level.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+</node>
+<node name="domain-password">
+ <properties>
+ <help>Set the authentication password for a routing domain</help>
+ </properties>
+ <children>
+ <leafNode name="plaintext-password">
+ <properties>
+ <help>Plain-text authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Level-wide password</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="md5">
+ <properties>
+ <help>MD5 authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Level-wide password</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<leafNode name="dynamic-hostname">
+ <properties>
+ <help>Dynamic hostname for IS-IS</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="level">
+ <properties>
+ <help>IS-IS level number</help>
+ <completionHelp>
+ <list>level-1 level-1-2 level-2</list>
+ </completionHelp>
+ <valueHelp>
+ <format>level-1</format>
+ <description>Act as a station router</description>
+ </valueHelp>
+ <valueHelp>
+ <format>level-1-2</format>
+ <description>Act as both a station and an area router</description>
+ </valueHelp>
+ <valueHelp>
+ <format>level-2</format>
+ <description>Act as an area router</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(level-1|level-1-2|level-2)$</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="log-adjacency-changes">
+ <properties>
+ <help>Log adjacency state changes</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="lsp-gen-interval">
+ <properties>
+ <help>Minimum interval between regenerating same LSP</help>
+ <valueHelp>
+ <format>u32:1-120</format>
+ <description>Minimum interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-120"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="lsp-mtu">
+ <properties>
+ <help>Configure the maximum size of generated LSPs</help>
+ <valueHelp>
+ <format>u32:128-4352</format>
+ <description>Maximum size of generated LSPs</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 128-4352"/>
+ </constraint>
+ </properties>
+ <defaultValue>1497</defaultValue>
+</leafNode>
+<leafNode name="lsp-refresh-interval">
+ <properties>
+ <help>LSP refresh interval</help>
+ <valueHelp>
+ <format>u32:1-65235</format>
+ <description>LSP refresh interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65235"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="max-lsp-lifetime">
+ <properties>
+ <help>Maximum LSP lifetime</help>
+ <valueHelp>
+ <format>u32:350-65535</format>
+ <description>LSP lifetime in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="metric-style">
+ <properties>
+ <help>Use old-style (ISO 10589) or new-style packet formats</help>
+ <completionHelp>
+ <list>narrow transition wide</list>
+ </completionHelp>
+ <valueHelp>
+ <format>narrow</format>
+ <description>Use old style of TLVs with narrow metric</description>
+ </valueHelp>
+ <valueHelp>
+ <format>transition</format>
+ <description>Send and accept both styles of TLVs during transition</description>
+ </valueHelp>
+ <valueHelp>
+ <format>wide</format>
+ <description>Use new style of TLVs to carry wider metric</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(narrow|transition|wide)$</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="net">
+ <properties>
+ <help>A Network Entity Title for this process (ISO only)</help>
+ <valueHelp>
+ <format>XX.XXXX. ... .XXX.XX</format>
+ <description>Network entity title (NET)</description>
+ </valueHelp>
+ <constraint>
+ <regex>[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2}</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="purge-originator">
+ <properties>
+ <help>Use the RFC 6232 purge-originator</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<node name="traffic-engineering">
+ <properties>
+ <help>Show IS-IS neighbor adjacencies</help>
+ </properties>
+ <children>
+ <leafNode name="enable">
+ <properties>
+ <help>Enable MPLS traffic engineering extensions</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+<!--
+ <node name="inter-as">
+ <properties>
+ <help>MPLS traffic engineering inter-AS support</help>
+ </properties>
+ <children>
+ <leafNode name="level-1">
+ <properties>
+ <help>Area native mode self originate inter-AS LSP with L1 only flooding scope</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="level-1-2">
+ <properties>
+ <help>Area native mode self originate inter-AS LSP with L1 and L2 flooding scope</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="level-2">
+ <properties>
+ <help>Area native mode self originate inter-AS LSP with L2 only flooding scope</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="inter-as">
+ <properties>
+ <help>MPLS traffic engineering inter-AS support</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+-->
+ <leafNode name="address">
+ <properties>
+ <help>MPLS traffic engineering router ID</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="segment-routing">
+ <properties>
+ <help>Segment-Routing (SPRING) settings</help>
+ </properties>
+ <children>
+ <leafNode name="enable">
+ <properties>
+ <help>Enable segment-routing functionality</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="global-block">
+ <properties>
+ <help>Global block label range</help>
+ </properties>
+ <children>
+ <leafNode name="low-label-value">
+ <properties>
+ <help>The lower bound of the global block</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>MPLS label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="high-label-value">
+ <properties>
+ <help>The upper bound of the global block</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>MPLS label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+<!--
+ <node name="local-block">
+ <properties>
+ <help>Local Block label range</help>
+ </properties>
+ <children>
+ <leafNode name="low-label-value">
+ <properties>
+ <help>The lower bound of the local block</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>MPLS label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument=" range 16-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="high-label-value">
+ <properties>
+ <help>The upper bound of the local block</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>MPLS label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument=" range 16-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+-->
+ <leafNode name="maximum-label-depth">
+ <properties>
+ <help>Maximum MPLS labels allowed for this router</help>
+ <valueHelp>
+ <format>u32:1-16</format>
+ <description>MPLS label depth</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-16"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="prefix">
+ <properties>
+ <help>Static IPv4/IPv6 prefix segment/label mapping</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix segment</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 prefix segment</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="absolute">
+ <properties>
+ <help>Specify the absolute value of prefix segment/label ID</help>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Specify the absolute value of prefix segment/label ID</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>The absolute segment/label ID value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="explicit-null">
+ <properties>
+ <help>Request upstream neighbor to replace segment/label with explicit null label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="no-php-flag">
+ <properties>
+ <help>Do not request penultimate hop popping for segment/label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="index">
+ <properties>
+ <help>Specify the index value of prefix segment/label ID</help>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Specify the index value of prefix segment/label ID</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>The index segment/label ID value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="explicit-null">
+ <properties>
+ <help>Request upstream neighbor to replace segment/label with explicit null label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="no-php-flag">
+ <properties>
+ <help>Do not request penultimate hop popping for segment/label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+</node>
+<node name="redistribute">
+ <properties>
+ <help>Redistribute information from another routing protocol</help>
+ </properties>
+ <children>
+ <node name="ipv4">
+ <properties>
+ <help>Redistribute IPv4 routes</help>
+ </properties>
+ <children>
+ <node name="bgp">
+ <properties>
+ <help>Border Gateway Protocol (BGP)</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="connected">
+ <properties>
+ <help>Redistribute connected routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="kernel">
+ <properties>
+ <help>Redistribute kernel routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="ospf">
+ <properties>
+ <help>Redistribute OSPF routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="rip">
+ <properties>
+ <help>Redistribute RIP routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="static">
+ <properties>
+ <help>Redistribute static routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>Redistribute IPv6 routes</help>
+ </properties>
+ <children>
+ <node name="bgp">
+ <properties>
+ <help>Redistribute BGP routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="connected">
+ <properties>
+ <help>Redistribute connected routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="kernel">
+ <properties>
+ <help>Redistribute kernel routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="ospf6">
+ <properties>
+ <help>Redistribute OSPFv3 routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="ripng">
+ <properties>
+ <help>Redistribute RIPng routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ <node name="static">
+ <properties>
+ <help>Redistribute static routes into IS-IS</help>
+ </properties>
+ <children>
+ #include <include/isis/redistribute-level-1-2.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+</node>
+<leafNode name="set-attached-bit">
+ <properties>
+ <help>Set attached bit to identify as L1/L2 router for inter-area traffic</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="set-overload-bit">
+ <properties>
+ <help>Set overload bit to avoid any transit traffic</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<node name="spf-delay-ietf">
+ <properties>
+ <help>IETF SPF delay algorithm</help>
+ </properties>
+ <children>
+ <leafNode name="init-delay">
+ <properties>
+ <help>Delay used while in QUIET state</help>
+ <valueHelp>
+ <format>u32:0-60000</format>
+ <description>Delay used while in QUIET state (in ms)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-60000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="short-delay">
+ <properties>
+ <help>Delay used while in SHORT_WAIT state</help>
+ <valueHelp>
+ <format>u32:0-60000</format>
+ <description>Delay used while in SHORT_WAIT state (in ms)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-60000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="long-delay">
+ <properties>
+ <help>Delay used while in LONG_WAIT</help>
+ <valueHelp>
+ <format>u32:0-60000</format>
+ <description>Delay used while in LONG_WAIT state in ms</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-60000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="holddown">
+ <properties>
+ <help>Time with no received IGP events before considering IGP stable</help>
+ <valueHelp>
+ <format>u32:0-60000</format>
+ <description>Time with no received IGP events before considering IGP stable in ms</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-60000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="time-to-learn">
+ <properties>
+ <help>Maximum duration needed to learn all the events related to a single failure</help>
+ <valueHelp>
+ <format>u32:0-60000</format>
+ <description>Maximum duration needed to learn all the events related to a single failure in ms</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-60000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<leafNode name="spf-interval">
+ <properties>
+ <help>Minimum interval between SPF calculations</help>
+ <valueHelp>
+ <format>u32:1-120</format>
+ <description>Interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-120"/>
+ </constraint>
+ </properties>
+</leafNode>
+<tagNode name="interface">
+ <properties>
+ <help>Interface params</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <children>
+ #include <include/bfd.xml.i>
+ <leafNode name="circuit-type">
+ <properties>
+ <help>Configure circuit type for interface</help>
+ <completionHelp>
+ <list>level-1 level-1-2 level-2-only</list>
+ </completionHelp>
+ <valueHelp>
+ <format>level-1</format>
+ <description>Level-1 only adjacencies are formed</description>
+ </valueHelp>
+ <valueHelp>
+ <format>level-1-2</format>
+ <description>Level-1-2 adjacencies are formed</description>
+ </valueHelp>
+ <valueHelp>
+ <format>level-2-only</format>
+ <description>Level-2 only adjacencies are formed</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(level-1|level-1-2|level-2-only)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-padding">
+ <properties>
+ <help>Add padding to IS-IS hello packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-interval">
+ <properties>
+ <help>Set Hello interval</help>
+ <valueHelp>
+ <format>u32:1-600</format>
+ <description>Set Hello interval</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-600"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-multiplier">
+ <properties>
+ <help>Set Hello interval</help>
+ <valueHelp>
+ <format>u32:2-100</format>
+ <description>Set multiplier for Hello holding time</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-100"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/isis/metric.xml.i>
+ <node name="network">
+ <properties>
+ <help>Set network type</help>
+ </properties>
+ <children>
+ <leafNode name="point-to-point">
+ <properties>
+ <help>point-to-point network type</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ #include <include/isis/passive.xml.i>
+ <node name="password">
+ <properties>
+ <help>Configure the authentication password for a circuit</help>
+ </properties>
+ <children>
+ <leafNode name="plaintext-password">
+ <properties>
+ <help>Plain-text authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Circuit password</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="priority">
+ <properties>
+ <help>Set priority for Designated Router election</help>
+ <valueHelp>
+ <format>u32:0-127</format>
+ <description>Priority value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-127"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="psnp-interval">
+ <properties>
+ <help>Set PSNP interval</help>
+ <valueHelp>
+ <format>u32:0-127</format>
+ <description>PSNP interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-127"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="no-three-way-handshake">
+ <properties>
+ <help>Disable three-way handshake</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+#include <include/route-map.xml.i>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/isis/redistribute-level-1-2.xml.i b/interface-definitions/include/isis/redistribute-level-1-2.xml.i
new file mode 100644
index 000000000..abb85274f
--- /dev/null
+++ b/interface-definitions/include/isis/redistribute-level-1-2.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from isis/redistribute-level-1-2.xml.i -->
+<node name="level-1">
+ <properties>
+ <help>Redistribute into level-1</help>
+ </properties>
+ <children>
+ #include <include/isis/metric.xml.i>
+ #include <include/route-map.xml.i>
+ </children>
+</node>
+<node name="level-2">
+ <properties>
+ <help>Redistribute into level-2</help>
+ </properties>
+ <children>
+ #include <include/isis/metric.xml.i>
+ #include <include/route-map.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/accel-ppp/name-server.xml.i b/interface-definitions/include/name-server-ipv4-ipv6.xml.i
index e744b384f..14973234b 100644
--- a/interface-definitions/include/accel-ppp/name-server.xml.i
+++ b/interface-definitions/include/name-server-ipv4-ipv6.xml.i
@@ -1,7 +1,7 @@
-<!-- include start from accel-ppp/name-server.xml.i -->
+<!-- include start from name-server-ipv4-ipv6.xml.i -->
<leafNode name="name-server">
<properties>
- <help>Domain Name Server (DNS) propagated to client</help>
+ <help>Domain Name Servers (DNS) addresses</help>
<valueHelp>
<format>ipv4</format>
<description>Domain Name Server (DNS) IPv4 address</description>
diff --git a/interface-definitions/include/name-server-ipv4.xml.i b/interface-definitions/include/name-server-ipv4.xml.i
new file mode 100644
index 000000000..0cf884e03
--- /dev/null
+++ b/interface-definitions/include/name-server-ipv4.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from name-server-ipv4.xml.i -->
+<leafNode name="name-server">
+ <properties>
+ <help>Domain Name Servers (DNS) addresses</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Domain Name Server (DNS) IPv4 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/name-server-ipv6.xml.i b/interface-definitions/include/name-server-ipv6.xml.i
new file mode 100644
index 000000000..d4517c4c6
--- /dev/null
+++ b/interface-definitions/include/name-server-ipv6.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from name-server-ipv6.xml.i -->
+<leafNode name="name-server">
+ <properties>
+ <help>Domain Name Servers (DNS) addresses</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Domain Name Server (DNS) IPv6 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i
index 579d19bdd..084f1f722 100644
--- a/interface-definitions/include/nat-rule.xml.i
+++ b/interface-definitions/include/nat-rule.xml.i
@@ -278,7 +278,7 @@
<description>Robust Header Compression</description>
</valueHelp>
<valueHelp>
- <format>0-255</format>
+ <format>u32:0-255</format>
<description>IP protocol number</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/pppoe-access-concentrator.xml.i b/interface-definitions/include/pppoe-access-concentrator.xml.i
new file mode 100644
index 000000000..ccfcc1c49
--- /dev/null
+++ b/interface-definitions/include/pppoe-access-concentrator.xml.i
@@ -0,0 +1,11 @@
+<!-- include start from pppoe-access-concentrator.xml.i -->
+<leafNode name="access-concentrator">
+ <properties>
+ <help>Access concentrator name</help>
+ <constraint>
+ <regex>[a-zA-Z0-9]{1,100}</regex>
+ </constraint>
+ <constraintErrorMessage>Access-concentrator name must be alphanumerical only (max. 100 characters)</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/route-map.xml.i b/interface-definitions/include/route-map.xml.i
new file mode 100644
index 000000000..88092b7d4
--- /dev/null
+++ b/interface-definitions/include/route-map.xml.i
@@ -0,0 +1,18 @@
+<!-- include start from route-map.xml.i -->
+<leafNode name="route-map">
+ <properties>
+ <help>Specify route-map name to use</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Route map name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/source-address-ipv4.xml.i b/interface-definitions/include/source-address-ipv4.xml.i
index 86235df61..052678113 100644
--- a/interface-definitions/include/source-address-ipv4.xml.i
+++ b/interface-definitions/include/source-address-ipv4.xml.i
@@ -1,7 +1,7 @@
<!-- include start from source-address-ipv4.xml.i -->
<leafNode name="source-address">
<properties>
- <help>IPv4 source address used to initiiate connection</help>
+ <help>IPv4 source address used to initiate connection</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --ipv4</script>
</completionHelp>
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 6c5e2abcc..5a4f08bef 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -49,13 +49,13 @@
</leafNode>
</children>
</node>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
- #include <include/interface/interface-mirror.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
+ #include <include/interface/mirror.xml.i>
<leafNode name="hash-policy">
<properties>
<help>Bonding transmit hash policy</help>
@@ -89,9 +89,9 @@
</properties>
<defaultValue>layer2</defaultValue>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
+ #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>
<help>Minimum number of member interfaces required up before enabling bond</help>
@@ -177,18 +177,32 @@
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py --bondable</script>
</completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="interface-name"/>
+ </constraint>
<multi/>
</properties>
</leafNode>
</children>
</node>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #include <include/interface/mtu-68-16000.xml.i>
<leafNode name="primary">
<properties>
<help>Primary device interface</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py --bondable</script>
</completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="interface-name"/>
+ </constraint>
</properties>
</leafNode>
#include <include/interface/vif-s.xml.i>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 15feedca1..144f43f32 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -21,11 +21,11 @@
<properties>
<help>MAC address aging interval</help>
<valueHelp>
- <format>0</format>
+ <format>u32:0</format>
<description>Disable MAC address learning (always flood)</description>
</valueHelp>
<valueHelp>
- <format>10-1000000</format>
+ <format>u32:10-1000000</format>
<description>MAC address aging time in seconds (default: 300)</description>
</valueHelp>
<constraint>
@@ -34,18 +34,18 @@
</properties>
<defaultValue>300</defaultValue>
</leafNode>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #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>
<valueHelp>
- <format>0-200</format>
+ <format>u32:0-200</format>
<description>Spanning Tree Protocol forwarding delay in seconds (default 15)</description>
</valueHelp>
<constraint>
@@ -59,7 +59,7 @@
<properties>
<help>Hello packet advertisment interval</help>
<valueHelp>
- <format>1-10</format>
+ <format>u32:1-10</format>
<description>Spanning Tree Protocol hello advertisement interval in seconds (default 2)</description>
</valueHelp>
<constraint>
@@ -82,15 +82,21 @@
</leafNode>
</children>
</node>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mirror.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
+ #include <include/interface/mac.xml.i>
+ #include <include/interface/mirror.xml.i>
+ <leafNode name="enable-vlan">
+ <properties>
+ <help>Enable VLAN aware bridge</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="max-age">
<properties>
<help>Interval at which neighbor bridges are removed</help>
<valueHelp>
- <format>1-40</format>
+ <format>u32:1-40</format>
<description>Bridge maximum aging time in seconds (default 20)</description>
</valueHelp>
<constraint>
@@ -117,7 +123,7 @@
<properties>
<help>Specify VLAN id which should natively be present on the link</help>
<valueHelp>
- <format>1-4094</format>
+ <format>u32:1-4094</format>
<description>Virtual Local Area Network (VLAN) ID</description>
</valueHelp>
<constraint>
@@ -138,7 +144,7 @@
<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>
+ <validator name="allowed-vlan"/>
</constraint>
<constraintErrorMessage>not a valid VLAN ID value or range</constraintErrorMessage>
<multi/>
@@ -148,7 +154,7 @@
<properties>
<help>Bridge port cost</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Path cost value for Spanning Tree Protocol</description>
</valueHelp>
<constraint>
@@ -162,7 +168,7 @@
<properties>
<help>Bridge port priority</help>
<valueHelp>
- <format>0-63</format>
+ <format>u32:0-63</format>
<description>Bridge port priority</description>
</valueHelp>
<constraint>
@@ -172,6 +178,12 @@
</properties>
<defaultValue>32</defaultValue>
</leafNode>
+ <leafNode name="isolated">
+ <properties>
+ <help>Port is isolated (also known as Private-VLAN)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</tagNode>
</children>
@@ -180,7 +192,7 @@
<properties>
<help>Priority for this bridge</help>
<valueHelp>
- <format>0-65535</format>
+ <format>u32:0-65535</format>
<description>Bridge priority (default 32768)</description>
</valueHelp>
<constraint>
@@ -196,7 +208,6 @@
<valueless/>
</properties>
</leafNode>
- #include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in
index 84c6903c7..2bc88c1a7 100644
--- a/interface-definitions/interfaces-dummy.xml.in
+++ b/interface-definitions/interfaces-dummy.xml.in
@@ -17,17 +17,17 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.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/interface-source-validation.xml.i>
+ #include <include/interface/source-validation.xml.i>
</children>
</node>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 9857e6c8d..27d555552 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -17,7 +17,7 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
<leafNode name="disable-flow-control">
@@ -26,8 +26,8 @@
<valueless/>
</properties>
</leafNode>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
<leafNode name="duplex">
<properties>
<help>Duplex mode</help>
@@ -53,13 +53,13 @@
</properties>
<defaultValue>auto</defaultValue>
</leafNode>
- #include <include/interface/interface-eapol.xml.i>
- #include <include/interface/interface-hw-id.xml.i>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
- #include <include/interface/interface-mirror.xml.i>
+ #include <include/interface/eapol.xml.i>
+ #include <include/interface/hw-id.xml.i>
+ #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-16000.xml.i>
+ #include <include/interface/mirror.xml.i>
<node name="offload">
<properties>
<help>Configurable offload options</help>
@@ -101,12 +101,6 @@
<valueless/>
</properties>
</leafNode>
- <leafNode name="ufo">
- <properties>
- <help>Enable UDP Fragmentation Offloading</help>
- <valueless/>
- </properties>
- </leafNode>
</children>
</node>
<leafNode name="speed">
@@ -199,7 +193,7 @@
</node>
#include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index 50031b8b3..6620c8aff 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -17,12 +17,12 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mtu-1450-16000.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
+ #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-16000.xml.i>
#include <include/tunnel-remote.xml.i>
<leafNode name="vni">
<properties>
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in
index 2edf08070..1cd30b86e 100644
--- a/interface-definitions/interfaces-l2tpv3.xml.in
+++ b/interface-definitions/interfaces-l2tpv3.xml.in
@@ -17,12 +17,12 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
<leafNode name="destination-port">
<properties>
<help>UDP destination port for L2TPv3 tunnel (default: 5000)</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
@@ -31,7 +31,7 @@
</properties>
<defaultValue>5000</defaultValue>
</leafNode>
- #include <include/interface/interface-disable.xml.i>
+ #include <include/interface/disable.xml.i>
<leafNode name="encapsulation">
<properties>
<help>Encapsulation type (default: UDP)</help>
@@ -53,10 +53,10 @@
</properties>
<defaultValue>udp</defaultValue>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
#include <include/source-address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #include <include/interface/mtu-68-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1488</defaultValue>
</leafNode>
@@ -64,7 +64,7 @@
<properties>
<help>Peer session identifier</help>
<valueHelp>
- <format>1-429496729</format>
+ <format>u32:1-429496729</format>
<description>L2TPv3 peer session identifier</description>
</valueHelp>
<constraint>
@@ -76,7 +76,7 @@
<properties>
<help>Peer tunnel identifier</help>
<valueHelp>
- <format>1-429496729</format>
+ <format>u32:1-429496729</format>
<description>L2TPv3 peer tunnel identifier</description>
</valueHelp>
<constraint>
@@ -89,7 +89,7 @@
<properties>
<help>Session identifier</help>
<valueHelp>
- <format>1-429496729</format>
+ <format>u32:1-429496729</format>
<description>L2TPv3 session identifier</description>
</valueHelp>
<constraint>
@@ -101,7 +101,7 @@
<properties>
<help>UDP source port for L2TPv3 tunnel (default: 5000)</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
@@ -114,7 +114,7 @@
<properties>
<help>Local tunnel identifier</help>
<valueHelp>
- <format>1-429496729</format>
+ <format>u32:1-429496729</format>
<description>L2TPv3 local tunnel identifier</description>
</valueHelp>
<constraint>
@@ -122,7 +122,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in
index 5d0ca5b0a..7be15ab89 100644
--- a/interface-definitions/interfaces-loopback.xml.in
+++ b/interface-definitions/interfaces-loopback.xml.in
@@ -17,13 +17,13 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
<node name="ip">
<properties>
<help>IPv4 routing parameters</help>
</properties>
<children>
- #include <include/interface/interface-source-validation.xml.i>
+ #include <include/interface/source-validation.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index fce88b21c..4a566ef8b 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -17,8 +17,8 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.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>
@@ -82,7 +82,7 @@
<properties>
<help>Priority of MACsec Key Agreement protocol (MKA) actor (default: 255)</help>
<valueHelp>
- <format>0-255</format>
+ <format>u32:0-255</format>
<description>MACsec Key Agreement protocol (MKA) priority</description>
</valueHelp>
<constraint>
@@ -97,11 +97,11 @@
<properties>
<help>IEEE 802.1X/MACsec replay protection window</help>
<valueHelp>
- <format>0</format>
+ <format>u32:0</format>
<description>No replay window, strict check</description>
</valueHelp>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>Number of packets that could be misordered</description>
</valueHelp>
<constraint>
@@ -111,14 +111,14 @@
</leafNode>
</children>
</node>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/mtu-68-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1460</defaultValue>
</leafNode>
#include <include/source-interface-ethernet.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 681290570..51e81390c 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -33,7 +33,7 @@
</leafNode>
</children>
</node>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
<leafNode name="device-type">
<properties>
<help>OpenVPN interface device-type (default: tun)</help>
@@ -54,7 +54,7 @@
</properties>
<defaultValue>tun</defaultValue>
</leafNode>
- #include <include/interface/interface-disable.xml.i>
+ #include <include/interface/disable.xml.i>
<node name="encryption">
<properties>
<help>Data Encryption settings</help>
@@ -165,7 +165,7 @@
</leafNode>
</children>
</node>
- #include <include/interface/interface-ipv6-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
<leafNode name="hash">
<properties>
<help>Hashing Algorithm</help>
@@ -206,7 +206,7 @@
<properties>
<help>Maximum number of keepalive packet failures (default: 60)</help>
<valueHelp>
- <format>0-1000</format>
+ <format>u32:0-1000</format>
<description>Maximum number of keepalive packet failures</description>
</valueHelp>
<constraint>
@@ -219,7 +219,7 @@
<properties>
<help>Keepalive packet interval in seconds (default: 10)</help>
<valueHelp>
- <format>0-600</format>
+ <format>u32:0-600</format>
<description>Keepalive packet interval (seconds)</description>
</valueHelp>
<constraint>
@@ -268,7 +268,7 @@
<properties>
<help>Local port number to accept connections</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
@@ -378,7 +378,7 @@
<properties>
<help>Remote port number to connect to</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<constraint>
@@ -546,7 +546,7 @@
<properties>
<help>Number of maximum client connections</help>
<valueHelp>
- <format>1-4096</format>
+ <format>u32:1-4096</format>
<description>Number of concurrent clients</description>
</valueHelp>
<constraint>
@@ -554,24 +554,8 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="name-server">
- <properties>
- <help>Domain Name Server (DNS)</help>
- <valueHelp>
- <format>ipv4</format>
- <description>DNS server IPv4 address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>DNS server IPv6 address</description>
- </valueHelp>
- <constraint>
- <validator name="ip-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="push-route">
+ #include <include/name-server-ipv4-ipv6.xml.i>
+ <tagNode name="push-route">
<properties>
<help>Route to be pushed to all clients</help>
<valueHelp>
@@ -585,9 +569,23 @@
<constraint>
<validator name="ip-prefix"/>
</constraint>
- <multi/>
</properties>
- </leafNode>
+ <children>
+ <leafNode name="metric">
+ <properties>
+ <help>Set metric for this route</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Metric for this route</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ <defaultValue>0</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
<leafNode name="reject-unconfigured-clients">
<properties>
<help>Reject connections from clients that are not explicitly configured</help>
@@ -755,7 +753,7 @@
<valueless/>
</properties>
</leafNode>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index 96479e057..198a53c90 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -5,7 +5,7 @@
<tagNode name="pppoe" owner="${vyos_conf_scripts_dir}/interfaces-pppoe.py">
<properties>
<help>Point-to-Point Protocol over Ethernet (PPPoE)</help>
- <priority>321</priority>
+ <priority>322</priority>
<constraint>
<regex>^pppoe[0-9]+$</regex>
</constraint>
@@ -16,17 +16,9 @@
</valueHelp>
</properties>
<children>
- <leafNode name="access-concentrator">
- <properties>
- <help>Access concentrator name (only connect to this concentrator)</help>
- <constraint>
- <regex>[a-zA-Z0-9]+$</regex>
- </constraint>
- <constraintErrorMessage>Access concentrator name must be composed of uppper and lower case letters or numbers only</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/pppoe-access-concentrator.xml.i>
#include <include/interface/authentication.xml.i>
- #include <include/interface/interface-dial-on-demand.xml.i>
+ #include <include/interface/dial-on-demand.xml.i>
<leafNode name="default-route">
<properties>
<help>Default route insertion behaviour (default: auto)</help>
@@ -53,16 +45,20 @@
<defaultValue>auto</defaultValue>
</leafNode>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
<leafNode name="idle-timeout">
<properties>
<help>Delay before disconnecting idle session (in seconds)</help>
<valueHelp>
- <format>n</format>
+ <format>u32:0-86400</format>
<description>Idle timeout in seconds</description>
</valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-86400"/>
+ </constraint>
+ <constraintErrorMessage>Timeout must be in range 0 to 86400</constraintErrorMessage>
</properties>
</leafNode>
<node name="ip">
@@ -70,7 +66,7 @@
<help>IPv4 routing parameters</help>
</properties>
<children>
- #include <include/interface/interface-source-validation.xml.i>
+ #include <include/interface/source-validation.xml.i>
</children>
</node>
<node name="ipv6">
@@ -88,14 +84,7 @@
</node>
</children>
</node>
- <leafNode name="source-interface">
- <properties>
- <help>Physical Interface used for this PPPoE session</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
- </completionHelp>
- </properties>
- </leafNode>
+ #include <include/source-interface.xml.i>
<leafNode name="local-address">
<properties>
<help>IPv4 address of local end of the PPPoE link</help>
@@ -108,7 +97,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/interface-mtu-68-1500.xml.i>
+ #include <include/interface/mtu-68-1500.xml.i>
<leafNode name="mtu">
<defaultValue>1492</defaultValue>
</leafNode>
@@ -136,7 +125,7 @@
<constraint>
<regex>[a-zA-Z0-9]+$</regex>
</constraint>
- <constraintErrorMessage>Service name must be composed of uppper and lower case letters or numbers only</constraintErrorMessage>
+ <constraintErrorMessage>Service name must be alphanumeric only</constraintErrorMessage>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index 136841290..366892032 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -17,16 +17,16 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
#include <include/source-interface-ethernet.xml.i>
- #include <include/interface/interface-mac.xml.i>
+ #include <include/interface/mac.xml.i>
<leafNode name="mode">
<properties>
<help>Receive mode (default: private)</help>
@@ -56,7 +56,7 @@
</properties>
<defaultValue>private</defaultValue>
</leafNode>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #include <include/interface/mtu-68-16000.xml.i>
#include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
</children>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index bc1f3092d..2c15abec7 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -16,17 +16,17 @@
</valueHelp>
</properties>
<children>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-vrf.xml.i>
- #include <include/interface/interface-mtu-64-8024.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/vrf.xml.i>
+ #include <include/interface/mtu-64-8024.xml.i>
<leafNode name="mtu">
<defaultValue>1476</defaultValue>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
#include <include/source-address-ipv4-ipv6.xml.i>
#include <include/tunnel-remote.xml.i>
#include <include/source-interface.xml.i>
@@ -54,21 +54,6 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="dhcp-interface">
- <properties>
- <help>dhcp interface</help>
- <valueHelp>
- <format>interface</format>
- <description>DHCP interface that supplies the local IP address for this tunnel</description>
- </valueHelp>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- <constraint>
- <validator name="interface-name"/>
- </constraint>
- </properties>
- </leafNode>
<leafNode name="encapsulation">
<properties>
<help>Encapsulation of this tunnel interface</help>
@@ -151,9 +136,9 @@
<valueless/>
</properties>
</leafNode>
- #include <include/interface/interface-parameters-key.xml.i>
- #include <include/interface/interface-parameters-tos.xml.i>
- #include <include/interface/interface-parameters-ttl.xml.i>
+ #include <include/interface/parameters-key.xml.i>
+ #include <include/interface/parameters-tos.xml.i>
+ #include <include/interface/parameters-ttl.xml.i>
<leafNode name="ttl">
<defaultValue>64</defaultValue>
</leafNode>
@@ -171,8 +156,8 @@
<list>none</list>
</completionHelp>
<valueHelp>
- <format>0-255</format>
- <description>Encaplimit (default 4)</description>
+ <format>u32:0-255</format>
+ <description>Encaplimit (default: 4)</description>
</valueHelp>
<valueHelp>
<format>none</format>
@@ -186,12 +171,12 @@
</properties>
<defaultValue>4</defaultValue>
</leafNode>
- #include <include/interface/interface-parameters-flowlabel.xml.i>
+ #include <include/interface/parameters-flowlabel.xml.i>
<leafNode name="hoplimit">
<properties>
<help>Hoplimit</help>
<valueHelp>
- <format>0-255</format>
+ <format>u32:0-255</format>
<description>Hoplimit (default 64)</description>
</valueHelp>
<constraint>
@@ -205,7 +190,7 @@
<properties>
<help>Traffic class (Tclass)</help>
<valueHelp>
- <format>0x0-0x0FFFFF</format>
+ <format>0x0-0x0fffff</format>
<description>Traffic class, 'inherit' or hex value</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 848c6259e..6dcc3f5a0 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -17,8 +17,8 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
<leafNode name="group">
<properties>
<help>Multicast group address for VXLAN interface</help>
@@ -31,14 +31,16 @@
<description>Multicast IPv6 group address</description>
</valueHelp>
<constraint>
- <validator name="ip-address"/>
+ <validator name="ipv4-multicast"/>
+ <validator name="ipv6-multicast"/>
</constraint>
+ <constraintErrorMessage>Multicast IPv4/IPv6 address required</constraintErrorMessage>
</properties>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-mac.xml.i>
- #include <include/interface/interface-mtu-1200-16000.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
+ #include <include/interface/mac.xml.i>
+ #include <include/interface/mtu-1200-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1450</defaultValue>
</leafNode>
@@ -70,7 +72,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index 378251fed..73328c16a 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -17,16 +17,16 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6.xml.i>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
#include <include/port-number.xml.i>
- #include <include/interface/interface-mtu-68-16000.xml.i>
+ #include <include/interface/mtu-68-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1420</defaultValue>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
<leafNode name="fwmark">
<properties>
<help>A 32-bit fwmark value set on all outgoing packets</help>
@@ -118,7 +118,7 @@
<properties>
<help>Interval to send keepalive messages</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Interval in seconds</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index aaeb285f1..048c7b475 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -206,7 +206,7 @@
<properties>
<help>Number of antennas on this card</help>
<valueHelp>
- <format>1-8</format>
+ <format>u32:1-8</format>
<description>Number of antennas for this card</description>
</valueHelp>
<constraint>
@@ -464,7 +464,7 @@
<constraintErrorMessage>Invalid ISO/IEC 3166-1 Country Code</constraintErrorMessage>
</properties>
</leafNode>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
<leafNode name="disable-broadcast-ssid">
@@ -473,25 +473,25 @@
<valueless/>
</properties>
</leafNode>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
<leafNode name="expunge-failing-stations">
<properties>
<help>Disassociate stations based on excessive transmission failures</help>
<valueless/>
</properties>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-hw-id.xml.i>
+ #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>
<help>Isolate stations on the AP so they cannot see each other</help>
<valueless/>
</properties>
</leafNode>
- #include <include/interface/interface-mac.xml.i>
+ #include <include/interface/mac.xml.i>
<leafNode name="max-stations">
<properties>
<help>Maximum number of wireless radio stations. Excess stations will be rejected upon authentication request.</help>
diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in
index 647ce0bc1..19f152a06 100644
--- a/interface-definitions/interfaces-wwan.xml.in
+++ b/interface-definitions/interfaces-wwan.xml.in
@@ -28,17 +28,17 @@
#include <include/interface/authentication.xml.i>
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
- #include <include/interface/interface-vrf.xml.i>
- #include <include/interface/interface-disable-link-detect.xml.i>
- #include <include/interface/interface-mtu-68-1500.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/vrf.xml.i>
+ #include <include/interface/disable-link-detect.xml.i>
+ #include <include/interface/mtu-68-1500.xml.i>
<leafNode name="mtu">
<defaultValue>1430</defaultValue>
</leafNode>
- #include <include/interface/interface-ipv4-options.xml.i>
- #include <include/interface/interface-ipv6-options.xml.i>
- #include <include/interface/interface-dial-on-demand.xml.i>
+ #include <include/interface/ipv4-options.xml.i>
+ #include <include/interface/ipv6-options.xml.i>
+ #include <include/interface/dial-on-demand.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/ipsec-settings.xml.in b/interface-definitions/ipsec-settings.xml.in
index bc54baa27..dbf6625fb 100644
--- a/interface-definitions/ipsec-settings.xml.in
+++ b/interface-definitions/ipsec-settings.xml.in
@@ -4,7 +4,7 @@
<children>
<node name="ipsec">
<children>
- <node name="options" owner="${vyos_conf_scripts_dir}/ipsec-settings.py">
+ <node name="options" owner="${vyos_conf_scripts_dir}/ipsec-settings.py from-options">
<properties>
<help>Global IPsec settings</help>
</properties>
diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in
index e14abae14..32ef0ad14 100644
--- a/interface-definitions/lldp.xml.in
+++ b/interface-definitions/lldp.xml.in
@@ -105,7 +105,7 @@
<properties>
<help>ECS ELIN (Emergency location identifier number)</help>
<valueHelp>
- <format>0-9999999999</format>
+ <format>u32:0-9999999999</format>
<description>Emergency Call Service ELIN number (between 10-25 numbers)</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in
index 2bfac900b..a518a9def 100644
--- a/interface-definitions/ntp.xml.in
+++ b/interface-definitions/ntp.xml.in
@@ -82,7 +82,7 @@
</children>
</node>
#include <include/listen-address.xml.i>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/protocols-igmp.xml.in b/interface-definitions/protocols-igmp.xml.in
index a9b11e1a3..e10340512 100644
--- a/interface-definitions/protocols-igmp.xml.in
+++ b/interface-definitions/protocols-igmp.xml.in
@@ -46,9 +46,16 @@
<leafNode name="version">
<properties>
<help>IGMP version</help>
+ <completionHelp>
+ <list>2 3</list>
+ </completionHelp>
<valueHelp>
- <format>2-3</format>
- <description>IGMP version</description>
+ <format>2</format>
+ <description>IGMP version 2</description>
+ </valueHelp>
+ <valueHelp>
+ <format>3</format>
+ <description>IGMP version 3</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 2-3"/>
@@ -59,7 +66,7 @@
<properties>
<help>IGMP host query interval</help>
<valueHelp>
- <format>1-1800</format>
+ <format>u32:1-1800</format>
<description>Query interval in seconds</description>
</valueHelp>
<constraint>
@@ -71,7 +78,7 @@
<properties>
<help>IGMP max query response time</help>
<valueHelp>
- <format>10-250</format>
+ <format>u32:10-250</format>
<description>Query response value in deci-seconds</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in
index 624c72a4c..e0bc47bb9 100644
--- a/interface-definitions/protocols-isis.xml.in
+++ b/interface-definitions/protocols-isis.xml.in
@@ -2,781 +2,15 @@
<interfaceDefinition>
<node name="protocols">
<children>
- <tagNode name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py">
+ <node name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py">
<properties>
<help>Intermediate System to Intermediate System (IS-IS)</help>
<priority>610</priority>
- <valueHelp>
- <format>text(TAG)</format>
- <description>ISO Routing area tag</description>
- </valueHelp>
</properties>
<children>
- <node name="area-password">
- <properties>
- <help>Configure the authentication password for an area</help>
- </properties>
- <children>
- <leafNode name="plaintext-password">
- <properties>
- <help>Plain-text authentication type</help>
- <valueHelp>
- <format>txt</format>
- <description>Level-wide password</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="md5">
- <properties>
- <help>MD5 authentication type</help>
- <valueHelp>
- <format>txt</format>
- <description>Level-wide password</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="default-information">
- <properties>
- <help>Control distribution of default information</help>
- </properties>
- <children>
- <node name="originate">
- <properties>
- <help>Distribute a default route</help>
- </properties>
- <children>
- <node name="ipv4">
- <properties>
- <help>Distribute default route for IPv4</help>
- </properties>
- <children>
- <leafNode name="level-1">
- <properties>
- <help>Distribute default route into level-1</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="level-2">
- <properties>
- <help>Distribute default route into level-2</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="ipv6">
- <properties>
- <help>Distribute default route for IPv6</help>
- </properties>
- <children>
- <leafNode name="level-1">
- <properties>
- <help>Distribute default route into level-1</help>
- <completionHelp>
- <list>always</list>
- </completionHelp>
- <valueHelp>
- <format>always</format>
- <description>Always advertise default route</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="level-2">
- <properties>
- <help>Distribute default route into level-2</help>
- <completionHelp>
- <list>always</list>
- </completionHelp>
- <valueHelp>
- <format>always</format>
- <description>Always advertise default route</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
- <node name="domain-password">
- <properties>
- <help>Set the authentication password for a routing domain</help>
- </properties>
- <children>
- <leafNode name="plaintext-password">
- <properties>
- <help>Plain-text authentication type</help>
- <valueHelp>
- <format>txt</format>
- <description>Level-wide password</description>
- </valueHelp>
- </properties>
- </leafNode>
-<!--
- <leafNode name="md5">
- <properties>
- <help>MD5 authentication type</help>
- <valueHelp>
- <format>txt</format>
- <description>Level-wide password</description>
- </valueHelp>
- </properties>
- </leafNode>
--->
- </children>
- </node>
- <leafNode name="dynamic-hostname">
- <properties>
- <help>Dynamic hostname for IS-IS</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="level">
- <properties>
- <help>IS-IS level number</help>
- <completionHelp>
- <list>level-1 level-1-2 level-2</list>
- </completionHelp>
- <valueHelp>
- <format>level-1</format>
- <description>Act as a station router</description>
- </valueHelp>
- <valueHelp>
- <format>level-1-2</format>
- <description>Act as both a station and an area router</description>
- </valueHelp>
- <valueHelp>
- <format>level-2</format>
- <description>Act as an area router</description>
- </valueHelp>
- <constraint>
- <regex>^(level-1|level-1-2|level-2)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="lsp-gen-interval">
- <properties>
- <help>Minimum interval between regenerating same LSP</help>
- <valueHelp>
- <format>u32:1-120</format>
- <description>Minimum interval in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-120"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="lsp-mtu">
- <properties>
- <help>Configure the maximum size of generated LSPs</help>
- <valueHelp>
- <format>u32:128-4352</format>
- <description>Maximum size of generated LSPs</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 128-4352"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="lsp-refresh-interval">
- <properties>
- <help>LSP refresh interval</help>
- <valueHelp>
- <format>u32:1-65235</format>
- <description>LSP refresh interval in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65235"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="max-lsp-lifetime">
- <properties>
- <help>Maximum LSP lifetime</help>
- <valueHelp>
- <format>u32:350-65535</format>
- <description>LSP lifetime in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="metric-style">
- <properties>
- <help>Use old-style (ISO 10589) or new-style packet formats</help>
- <completionHelp>
- <list>narrow transition wide</list>
- </completionHelp>
- <valueHelp>
- <format>narrow</format>
- <description>Use old style of TLVs with narrow metric</description>
- </valueHelp>
- <valueHelp>
- <format>transition</format>
- <description>Send and accept both styles of TLVs during transition</description>
- </valueHelp>
- <valueHelp>
- <format>wide</format>
- <description>Use new style of TLVs to carry wider metric</description>
- </valueHelp>
- <constraint>
- <regex>^(narrow|transition|wide)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="net">
- <properties>
- <help>A Network Entity Title for this process (ISO only)</help>
- <valueHelp>
- <format>XX.XXXX. ... .XXX.XX</format>
- <description>Network entity title (NET)</description>
- </valueHelp>
- <constraint>
- <regex>[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2}</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="purge-originator">
- <properties>
- <help>Use the RFC 6232 purge-originator</help>
- <valueless/>
- </properties>
- </leafNode>
- <node name="traffic-engineering">
- <properties>
- <help>Show IS-IS neighbor adjacencies</help>
- </properties>
- <children>
- <leafNode name="enable">
- <properties>
- <help>Enable MPLS traffic engineering extensions</help>
- <valueless/>
- </properties>
- </leafNode>
-<!--
- <node name="inter-as">
- <properties>
- <help>MPLS traffic engineering inter-AS support</help>
- </properties>
- <children>
- <leafNode name="level-1">
- <properties>
- <help>Area native mode self originate inter-AS LSP with L1 only flooding scope</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="level-1-2">
- <properties>
- <help>Area native mode self originate inter-AS LSP with L1 and L2 flooding scope</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="level-2">
- <properties>
- <help>Area native mode self originate inter-AS LSP with L2 only flooding scope</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="inter-as">
- <properties>
- <help>MPLS traffic engineering inter-AS support</help>
- <valueless/>
- </properties>
- </leafNode>
--->
- <leafNode name="address">
- <properties>
- <help>MPLS traffic engineering router ID</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="segment-routing">
- <properties>
- <help>Segment-Routing (SPRING) settings</help>
- </properties>
- <children>
- <leafNode name="enable">
- <properties>
- <help>Enable segment-routing functionality</help>
- <valueless/>
- </properties>
- </leafNode>
- <node name="global-block">
- <properties>
- <help>Global block label range</help>
- </properties>
- <children>
- <leafNode name="low-label-value">
- <properties>
- <help>The lower bound of the global block</help>
- <valueHelp>
- <format>u32:16-1048575</format>
- <description>MPLS label value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 16-1048575"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="high-label-value">
- <properties>
- <help>The upper bound of the global block</help>
- <valueHelp>
- <format>u32:16-1048575</format>
- <description>MPLS label value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 16-1048575"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
-<!--
- <node name="local-block">
- <properties>
- <help>Local Block label range</help>
- </properties>
- <children>
- <leafNode name="low-label-value">
- <properties>
- <help>The lower bound of the local block</help>
- <valueHelp>
- <format>u32:16-1048575</format>
- <description>MPLS label value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument=" range 16-1048575"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="high-label-value">
- <properties>
- <help>The upper bound of the local block</help>
- <valueHelp>
- <format>u32:16-1048575</format>
- <description>MPLS label value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument=" range 16-1048575"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
--->
- <leafNode name="maximum-label-depth">
- <properties>
- <help>Maximum MPLS labels allowed for this router</help>
- <valueHelp>
- <format>u32:1-16</format>
- <description>MPLS label depth</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-16"/>
- </constraint>
- </properties>
- </leafNode>
- <tagNode name="prefix">
- <properties>
- <help>Static IPv4/IPv6 prefix segment/label mapping</help>
- <valueHelp>
- <format>ipv4net</format>
- <description>IPv4 prefix segment</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6net</format>
- <description>IPv6 prefix segment</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-prefix"/>
- <validator name="ipv6-prefix"/>
- </constraint>
- </properties>
- <children>
- <node name="absolute">
- <properties>
- <help>Specify the absolute value of prefix segment/label ID</help>
- </properties>
- <children>
- <leafNode name="value">
- <properties>
- <help>Specify the absolute value of prefix segment/label ID</help>
- <valueHelp>
- <format>u32:16-1048575</format>
- <description>The absolute segment/label ID value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 16-1048575"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="explicit-null">
- <properties>
- <help>Request upstream neighbor to replace segment/label with explicit null label</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="no-php-flag">
- <properties>
- <help>Do not request penultimate hop popping for segment/label</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="index">
- <properties>
- <help>Specify the index value of prefix segment/label ID</help>
- </properties>
- <children>
- <leafNode name="value">
- <properties>
- <help>Specify the index value of prefix segment/label ID</help>
- <valueHelp>
- <format>u32:0-65535</format>
- <description>The index segment/label ID value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-65535"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="explicit-null">
- <properties>
- <help>Request upstream neighbor to replace segment/label with explicit null label</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="no-php-flag">
- <properties>
- <help>Do not request penultimate hop popping for segment/label</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
- </children>
- </node>
- <node name="redistribute">
- <properties>
- <help>Redistribute information from another routing protocol</help>
- </properties>
- <children>
- <node name="ipv4">
- <properties>
- <help>Redistribute IPv4 routes</help>
- </properties>
- <children>
- <node name="bgp">
- <properties>
- <help>Border Gateway Protocol (BGP)</help>
- </properties>
- <children>
- #include <include/isis-redistribute-ipv4.xml.i>
- </children>
- </node>
- <node name="connected">
- <properties>
- <help>Redistribute connected routes into IS-IS</help>
- </properties>
- <children>
- #include <include/isis-redistribute-ipv4.xml.i>
- </children>
- </node>
- <node name="kernel">
- <properties>
- <help>Redistribute kernel routes into IS-IS</help>
- </properties>
- <children>
- #include <include/isis-redistribute-ipv4.xml.i>
- </children>
- </node>
- <node name="ospf">
- <properties>
- <help>Redistribute OSPF routes into IS-IS</help>
- </properties>
- <children>
- #include <include/isis-redistribute-ipv4.xml.i>
- </children>
- </node>
- <node name="rip">
- <properties>
- <help>Redistribute RIP routes into IS-IS</help>
- </properties>
- <children>
- #include <include/isis-redistribute-ipv4.xml.i>
- </children>
- </node>
- <node name="static">
- <properties>
- <help>Redistribute static routes into IS-IS</help>
- </properties>
- <children>
- #include <include/isis-redistribute-ipv4.xml.i>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
- <leafNode name="set-attached-bit">
- <properties>
- <help>Set attached bit to identify as L1/L2 router for inter-area traffic</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="set-overload-bit">
- <properties>
- <help>Set overload bit to avoid any transit traffic</help>
- <valueless/>
- </properties>
- </leafNode>
- <node name="spf-delay-ietf">
- <properties>
- <help>IETF SPF delay algorithm</help>
- </properties>
- <children>
- <leafNode name="init-delay">
- <properties>
- <help>Delay used while in QUIET state</help>
- <valueHelp>
- <format>u32:0-60000</format>
- <description>Delay used while in QUIET state (in ms)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-60000"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="short-delay">
- <properties>
- <help>Delay used while in SHORT_WAIT state</help>
- <valueHelp>
- <format>u32:0-60000</format>
- <description>Delay used while in SHORT_WAIT state (in ms)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-60000"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="long-delay">
- <properties>
- <help>Delay used while in LONG_WAIT</help>
- <valueHelp>
- <format>u32:0-60000</format>
- <description>Delay used while in LONG_WAIT state (in ms)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-60000"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="holddown">
- <properties>
- <help>Time with no received IGP events before considering IGP stable</help>
- <valueHelp>
- <format>u32:0-60000</format>
- <description>Time with no received IGP events before considering IGP stable (in ms)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-60000"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="time-to-learn">
- <properties>
- <help>Maximum duration needed to learn all the events related to a single failure</help>
- <valueHelp>
- <format>u32:0-60000</format>
- <description>Maximum duration needed to learn all the events related to a single failure (in ms)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-60000"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="spf-interval">
- <properties>
- <help>Minimum interval between SPF calculations</help>
- <valueHelp>
- <format>u32:1-120</format>
- <description>Minimum interval between consecutive SPFs in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-120"/>
- </constraint>
- </properties>
- </leafNode>
- <tagNode name="interface">
- <!-- (config-if)# ip router isis WORD (same as name of IS-IS process)
- if any section of "interface" pesent -->
- <properties>
- <help>Interface params</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- </properties>
- <children>
- <leafNode name="bfd">
- <properties>
- <help>Enable BFD support</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="circuit-type">
- <properties>
- <help>Configure circuit type for interface</help>
- <completionHelp>
- <list>level-1 level-1-2 level-2-only</list>
- </completionHelp>
- <valueHelp>
- <format>level-1</format>
- <description>Level-1 only adjacencies are formed</description>
- </valueHelp>
- <valueHelp>
- <format>level-1-2</format>
- <description>Level-1-2 adjacencies are formed</description>
- </valueHelp>
- <valueHelp>
- <format>level-2-only</format>
- <description>Level-2 only adjacencies are formed</description>
- </valueHelp>
- <constraint>
- <regex>^(level-1|level-1-2|level-2-only)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="hello-padding">
- <properties>
- <help>Add padding to IS-IS hello packets</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="hello-interval">
- <properties>
- <help>Set Hello interval</help>
- <valueHelp>
- <format>u32:1-600</format>
- <description>Set Hello interval</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-600"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="hello-multiplier">
- <properties>
- <help>Set Hello interval</help>
- <valueHelp>
- <format>u32:2-100</format>
- <description>Set multiplier for Hello holding time</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 2-100"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="metric">
- <properties>
- <help>Set default metric for circuit</help>
- <valueHelp>
- <format>u32:0-16777215</format>
- <description>Default metric value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-16777215"/>
- </constraint>
- </properties>
- </leafNode>
- <node name="network">
- <properties>
- <help>Set network type</help>
- </properties>
- <children>
- <leafNode name="point-to-point">
- <properties>
- <help>point-to-point network type</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="passive">
- <properties>
- <help>Configure the passive mode for interface</help>
- <valueless/>
- </properties>
- </leafNode>
- <node name="password">
- <properties>
- <help>Configure the authentication password for a circuit</help>
- </properties>
- <children>
- <leafNode name="plaintext-password">
- <properties>
- <help>Plain-text authentication type</help>
- <valueHelp>
- <format>txt</format>
- <description>Circuit password</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="priority">
- <properties>
- <help>Set priority for Designated Router election</help>
- <valueHelp>
- <format>u32:0-127</format>
- <description>Priority value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-127"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="psnp-interval">
- <properties>
- <help>Set PSNP interval in seconds</help>
- <valueHelp>
- <format>u32:0-127</format>
- <description>Priority value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-127"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="no-three-way-handshake">
- <properties>
- <help>Disable three-way handshake</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </tagNode>
+ #include <include/isis/protocol-common-config.xml.i>
</children>
- </tagNode>
+ </node>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/protocols-multicast.xml.in b/interface-definitions/protocols-multicast.xml.in
index a06f2b287..1b1382352 100644
--- a/interface-definitions/protocols-multicast.xml.in
+++ b/interface-definitions/protocols-multicast.xml.in
@@ -38,7 +38,7 @@
<properties>
<help>Distance value for this route</help>
<valueHelp>
- <format>1-255</format>
+ <format>u32:1-255</format>
<description>Distance for this route</description>
</valueHelp>
<constraint>
@@ -74,7 +74,7 @@
<properties>
<help>Distance value for this route</help>
<valueHelp>
- <format>1-255</format>
+ <format>u32:1-255</format>
<description>Distance for this route</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/protocols-pim.xml.in b/interface-definitions/protocols-pim.xml.in
index 6152045a7..bb5cc797b 100644
--- a/interface-definitions/protocols-pim.xml.in
+++ b/interface-definitions/protocols-pim.xml.in
@@ -21,7 +21,7 @@
<properties>
<help>Designated Router Election Priority</help>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>Value of the new DR Priority</description>
</valueHelp>
<constraint>
@@ -33,7 +33,7 @@
<properties>
<help>Hello Interval</help>
<valueHelp>
- <format>1-180</format>
+ <format>u32:1-180</format>
<description>Hello Interval in seconds</description>
</valueHelp>
<constraint>
@@ -79,7 +79,7 @@
<properties>
<help>Keep alive Timer</help>
<valueHelp>
- <format>31-60000</format>
+ <format>u32:31-60000</format>
<description>Keep alive Timer in seconds</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/service_console-server.xml.in b/interface-definitions/service_console-server.xml.in
index 78eb2d0ba..28aa7ea71 100644
--- a/interface-definitions/service_console-server.xml.in
+++ b/interface-definitions/service_console-server.xml.in
@@ -27,7 +27,7 @@
</constraint>
</properties>
<children>
- #include <include/interface/interface-description.xml.i>
+ #include <include/interface/description.xml.i>
<leafNode name="speed">
<properties>
<help>Serial port baud rate</help>
diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in
index 7c575ba77..b19acab56 100644
--- a/interface-definitions/service_ipoe-server.xml.in
+++ b/interface-definitions/service_ipoe-server.xml.in
@@ -111,7 +111,7 @@
</leafNode>
</children>
</tagNode>
- #include <include/accel-ppp/name-server.xml.i>
+ #include <include/name-server-ipv4-ipv6.xml.i>
#include <include/accel-ppp/client-ipv6-pool.xml.i>
<node name="authentication">
<properties>
diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in
index 9d3420ed2..712e6549e 100644
--- a/interface-definitions/service_pppoe-server.xml.in
+++ b/interface-definitions/service_pppoe-server.xml.in
@@ -8,14 +8,8 @@
<priority>900</priority>
</properties>
<children>
+ #include <include/pppoe-access-concentrator.xml.i>
<leafNode name="access-concentrator">
- <properties>
- <help>Access concentrator name</help>
- <constraint>
- <regex>[a-zA-Z0-9]{1,100}</regex>
- </constraint>
- <constraintErrorMessage>access-concentrator name limited to alphanumerical characters only (max. 100)</constraintErrorMessage>
- </properties>
<defaultValue>vyos-ac</defaultValue>
</leafNode>
<node name="authentication">
@@ -65,7 +59,7 @@
</children>
</node>
#include <include/accel-ppp/client-ipv6-pool.xml.i>
- #include <include/accel-ppp/name-server.xml.i>
+ #include <include/name-server-ipv4-ipv6.xml.i>
<tagNode name="interface">
<properties>
<help>interface(s) to listen on</help>
@@ -129,7 +123,7 @@
<constraint>
<regex>[a-zA-Z0-9\-]{1,100}</regex>
</constraint>
- <constraintErrorMessage>servicename can contain aplhanumerical characters and dashes only (max. 100)</constraintErrorMessage>
+ <constraintErrorMessage>Service-name can contain aplhanumerical characters and dashes only (max. 100)</constraintErrorMessage>
<multi/>
</properties>
</leafNode>
@@ -273,7 +267,7 @@
<properties>
<help>PADO delays</help>
<valueHelp>
- <format>1-999999</format>
+ <format>u32:1-999999</format>
<description>Number in ms</description>
</valueHelp>
<constraint>
@@ -286,7 +280,7 @@
<properties>
<help>Number of sessions</help>
<valueHelp>
- <format>1-999999</format>
+ <format>u32:1-999999</format>
<description>Number of sessions</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in
index 750ae314c..0f4009f5c 100644
--- a/interface-definitions/service_router-advert.xml.in
+++ b/interface-definitions/service_router-advert.xml.in
@@ -20,12 +20,12 @@
<properties>
<help>Set Hop Count field of the IP header for outgoing packets (default: 64)</help>
<valueHelp>
- <format>1-255</format>
- <description>Value should represent current diameter of the Internet</description>
+ <format>u32:0</format>
+ <description>Unspecified (by this router)</description>
</valueHelp>
<valueHelp>
- <format>0</format>
- <description>Unspecified (by this router)</description>
+ <format>u32:1-255</format>
+ <description>Value should represent current diameter of the Internet</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-255"/>
@@ -38,7 +38,7 @@
<properties>
<help>Lifetime associated with the default router in units of seconds</help>
<valueHelp>
- <format>4-9000</format>
+ <format>u32:4-9000</format>
<description>Router Lifetime in seconds</description>
</valueHelp>
<valueHelp>
@@ -86,7 +86,7 @@
<properties>
<help>Link MTU value placed in RAs, exluded in RAs if unset</help>
<valueHelp>
- <format>1280-9000</format>
+ <format>u32:1280-9000</format>
<description>Link MTU value in RAs</description>
</valueHelp>
<constraint>
@@ -110,7 +110,7 @@
<properties>
<help>Maximum interval between unsolicited multicast RAs (default: 600)</help>
<valueHelp>
- <format>4-1800</format>
+ <format>u32:4-1800</format>
<description>Maximum interval in seconds</description>
</valueHelp>
<constraint>
@@ -124,7 +124,7 @@
<properties>
<help>Minimum interval between unsolicited multicast RAs</help>
<valueHelp>
- <format>3-1350</format>
+ <format>u32:3-1350</format>
<description>Minimum interval in seconds</description>
</valueHelp>
<constraint>
@@ -135,19 +135,7 @@
</leafNode>
</children>
</node>
- <leafNode name="name-server">
- <properties>
- <help>IPv6 address of recursive DNS server</help>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of DNS name server</description>
- </valueHelp>
- <constraint>
- <validator name="ipv6-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/name-server-ipv6.xml.i>
<leafNode name="other-config-flag">
<properties>
<help>Hosts use the administered (stateful) protocol for autoconfiguration of other (non-address) information</help>
@@ -173,7 +161,7 @@
<list>infinity</list>
</completionHelp>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>Time in seconds that the route will remain valid</description>
</valueHelp>
<valueHelp>
@@ -272,7 +260,7 @@
<list>infinity</list>
</completionHelp>
<valueHelp>
- <format>1-4294967295</format>
+ <format>u32:1-4294967295</format>
<description>Time in seconds that the prefix will remain valid</description>
</valueHelp>
<valueHelp>
@@ -292,12 +280,12 @@
<properties>
<help>Time, in milliseconds, that a node assumes a neighbor is reachable after having received a reachability confirmation</help>
<valueHelp>
- <format>1-3600000</format>
- <description>Reachable Time value in RAs (in milliseconds)</description>
+ <format>u32:0</format>
+ <description>Reachable Time unspecified by this router</description>
</valueHelp>
<valueHelp>
- <format>0</format>
- <description>Reachable Time unspecified by this router</description>
+ <format>u32:1-3600000</format>
+ <description>Reachable Time value in RAs (in milliseconds)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 1-3600000"/>
@@ -310,12 +298,12 @@
<properties>
<help>Time in milliseconds between retransmitted Neighbor Solicitation messages</help>
<valueHelp>
- <format>1-4294967295</format>
- <description>Minimum interval in milliseconds</description>
+ <format>u32:0</format>
+ <description>Time, in milliseconds, between retransmitted Neighbor Solicitation messages</description>
</valueHelp>
<valueHelp>
- <format>0</format>
- <description>Time, in milliseconds, between retransmitted Neighbor Solicitation messages</description>
+ <format>u32:1-4294967295</format>
+ <description>Minimum interval in milliseconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 1-4294967295"/>
diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in
index 7cb0f7ece..2fbac3df0 100644
--- a/interface-definitions/service_webproxy.xml.in
+++ b/interface-definitions/service_webproxy.xml.in
@@ -214,7 +214,7 @@
<properties>
<help>Cache peer options (default: "no-query default")</help>
<valueHelp>
- <format>text</format>
+ <format>txt</format>
<description>Cache peer options</description>
</valueHelp>
</properties>
diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in
index f57103eac..3cb736bf7 100644
--- a/interface-definitions/snmp.xml.in
+++ b/interface-definitions/snmp.xml.in
@@ -626,7 +626,7 @@
</tagNode>
</children>
</node>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index 54742f1d0..e3b9d16e1 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -138,7 +138,7 @@
<properties>
<help>Enable transmission of keepalives from server to client</help>
<valueHelp>
- <format>1-65535</format>
+ <format>u32:1-65535</format>
<description>Time interval in seconds for keepalive message</description>
</valueHelp>
<constraint>
@@ -146,7 +146,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/system-console.xml.in b/interface-definitions/system-console.xml.in
index 88f7f82a9..2897e5e97 100644
--- a/interface-definitions/system-console.xml.in
+++ b/interface-definitions/system-console.xml.in
@@ -74,6 +74,7 @@
<regex>^(1200|2400|4800|9600|19200|38400|57600|115200)$</regex>
</constraint>
</properties>
+ <defaultValue>115200</defaultValue>
</leafNode>
</children>
</tagNode>
diff --git a/interface-definitions/system-lcd.xml.in b/interface-definitions/system-lcd.xml.in
index 36116ae1b..4c9d5c92e 100644
--- a/interface-definitions/system-lcd.xml.in
+++ b/interface-definitions/system-lcd.xml.in
@@ -12,7 +12,7 @@
<properties>
<help>Model of the display attached to this system [REQUIRED]</help>
<completionHelp>
- <list>cfa-533 cfa-631 cfa-633 cfa-635 sdec</list>
+ <list>cfa-533 cfa-631 cfa-633 cfa-635 hd44780 sdec</list>
</completionHelp>
<valueHelp>
<format>cfa-533</format>
@@ -31,11 +31,15 @@
<description>Crystalfontz CFA-635</description>
</valueHelp>
<valueHelp>
+ <format>hd44780</format>
+ <description>Hitachi HD44780, Caswell Appliances</description>
+ </valueHelp>
+ <valueHelp>
<format>sdec</format>
<description>Lanner, Watchguard, Nexcom NSA, Sophos UTM appliances</description>
</valueHelp>
<constraint>
- <regex>^(cfa-533|cfa-631|cfa-633|cfa-635|sdec)$</regex>
+ <regex>^(cfa-533|cfa-631|cfa-633|cfa-635|hd44780|sdec)$</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
index 86db3f368..4bfe82268 100644
--- a/interface-definitions/system-login.xml.in
+++ b/interface-definitions/system-login.xml.in
@@ -12,7 +12,7 @@
<properties>
<help>Local user account information</help>
<constraint>
- <regex>[a-zA-Z0-9\-_\.]{1,100}</regex>
+ <regex>^[-_a-zA-Z0-9.]{1,100}</regex>
</constraint>
<constraintErrorMessage>Username contains illegal characters or\nexceeds 100 character limitation.</constraintErrorMessage>
</properties>
@@ -52,7 +52,10 @@
<children>
<leafNode name="key">
<properties>
- <help>Public key value (base64-encoded)</help>
+ <help>Public key value (Base64 encoded)</help>
+ <constraint>
+ <validator name="base64"/>
+ </constraint>
</properties>
</leafNode>
<leafNode name="options">
@@ -145,7 +148,7 @@
</leafNode>
</children>
</tagNode>
- #include <include/interface/interface-vrf.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
deleted file mode 100644
index 426d7e71c..000000000
--- a/interface-definitions/vpn_ipsec.xml.in
+++ /dev/null
@@ -1,1167 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="vpn">
- <children>
- <node name="nipsec" owner="${vyos_conf_scripts_dir}/vpn_ipsec.py">
- <properties>
- <help>VPN IP security (IPsec) parameters</help>
- </properties>
- <children>
- <leafNode name="auto-update">
- <properties>
- <help>Set auto-update interval for IPsec daemon</help>
- <valueHelp>
- <format>u32:30-65535</format>
- <description>Auto-update interval (s)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 30-65535"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="disable-uniqreqids">
- <properties>
- <help>Option to disable requirement for unique IDs in the Security Database</help>
- <valueless/>
- </properties>
- </leafNode>
- <tagNode name="esp-group">
- <properties>
- <help>Name of Encapsulating Security Payload (ESP) group</help>
- </properties>
- <children>
- <leafNode name="compression">
- <properties>
- <help>ESP compression</help>
- <completionHelp>
- <list>disable enable</list>
- </completionHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable ESP compression (default)</description>
- </valueHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable ESP compression</description>
- </valueHelp>
- <constraint>
- <regex>^(disable|enable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="lifetime">
- <properties>
- <help>ESP lifetime</help>
- <valueHelp>
- <format>u32:30-86400</format>
- <description>ESP lifetime in seconds (default 3600)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 30-86400"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="mode">
- <properties>
- <help>ESP mode</help>
- <completionHelp>
- <list>tunnel transport</list>
- </completionHelp>
- <valueHelp>
- <format>tunnel</format>
- <description>Tunnel mode (default)</description>
- </valueHelp>
- <valueHelp>
- <format>transport</format>
- <description>Transport mode</description>
- </valueHelp>
- <constraint>
- <regex>^(tunnel|transport)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="pfs">
- <properties>
- <help>ESP Perfect Forward Secrecy</help>
- <completionHelp>
- <list>enable dh-group1 dh-group2 dh-group5 dh-group14 dh-group15 dh-group16 dh-group17 dh-group18 dh-group19 dh-group20 dh-group21 dh-group22 dh-group23 dh-group24 dh-group25 dh-group26 dh-group27 dh-group28 dh-group29 dh-group30 dh-group31 dh-group32 disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable PFS. Use ike-groups dh-group (default)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group1</format>
- <description>Enable PFS. Use Diffie-Hellman group 1 (modp768)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group2</format>
- <description>Enable PFS. Use Diffie-Hellman group 2 (modp1024)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group5</format>
- <description>Enable PFS. Use Diffie-Hellman group 5 (modp1536)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group14</format>
- <description>Enable PFS. Use Diffie-Hellman group 14 (modp2048)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group15</format>
- <description>Enable PFS. Use Diffie-Hellman group 15 (modp3072)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group16</format>
- <description>Enable PFS. Use Diffie-Hellman group 16 (modp4096)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group17</format>
- <description>Enable PFS. Use Diffie-Hellman group 17 (modp6144)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group18</format>
- <description>Enable PFS. Use Diffie-Hellman group 18 (modp8192)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group19</format>
- <description>Enable PFS. Use Diffie-Hellman group 19 (ecp256)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group20</format>
- <description>Enable PFS. Use Diffie-Hellman group 20 (ecp384)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group21</format>
- <description>Enable PFS. Use Diffie-Hellman group 21 (ecp521)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group22</format>
- <description>Enable PFS. Use Diffie-Hellman group 22 (modp1024s160)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group23</format>
- <description>Enable PFS. Use Diffie-Hellman group 23 (modp2048s224)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group24</format>
- <description>Enable PFS. Use Diffie-Hellman group 24 (modp2048s256)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group25</format>
- <description>Enable PFS. Use Diffie-Hellman group 25 (ecp192)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group26</format>
- <description>Enable PFS. Use Diffie-Hellman group 26 (ecp224)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group27</format>
- <description>Enable PFS. Use Diffie-Hellman group 27 (ecp224bp)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group28</format>
- <description>Enable PFS. Use Diffie-Hellman group 28 (ecp256bp)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group29</format>
- <description>Enable PFS. Use Diffie-Hellman group 29 (ecp384bp)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group30</format>
- <description>Enable PFS. Use Diffie-Hellman group 30 (ecp512bp)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group31</format>
- <description>Enable PFS. Use Diffie-Hellman group 31 (curve25519)</description>
- </valueHelp>
- <valueHelp>
- <format>dh-group32</format>
- <description>Enable PFS. Use Diffie-Hellman group 32 (curve448)</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable PFS</description>
- </valueHelp>
- <constraint>
- <regex>^(enable|dh-group1|dh-group2|dh-group5|dh-group14|dh-group15|dh-group16|dh-group17|dh-group18|dh-group19|dh-group20|dh-group21|dh-group22|dh-group23|dh-group24|dh-group25|dh-group26|dh-group27|dh-group28|dh-group29|dh-group30|dh-group31|dh-group32|disable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <tagNode name="proposal">
- <properties>
- <help>ESP-group proposal [REQUIRED]</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>ESP-group proposal number</description>
- </valueHelp>
- </properties>
- <children>
- #include <include/vpn-ipsec-encryption.xml.i>
- #include <include/vpn-ipsec-hash.xml.i>
- </children>
- </tagNode>
- </children>
- </tagNode>
- <tagNode name="ike-group">
- <properties>
- <help>Name of Internet Key Exchange (IKE) group</help>
- </properties>
- <children>
- <leafNode name="close-action">
- <properties>
- <help>close-action_help</help>
- <completionHelp>
- <list>none hold clear restart</list>
- </completionHelp>
- <valueHelp>
- <format>none</format>
- <description>Set action to none (default)</description>
- </valueHelp>
- <valueHelp>
- <format>hold</format>
- <description>Set action to hold</description>
- </valueHelp>
- <valueHelp>
- <format>clear</format>
- <description>Set action to clear</description>
- </valueHelp>
- <valueHelp>
- <format>restart</format>
- <description>Set action to restart</description>
- </valueHelp>
- <constraint>
- <regex>^(none|hold|clear|restart)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <node name="dead-peer-detection">
- <properties>
- <help>Dead Peer Detection (DPD)</help>
- </properties>
- <children>
- <leafNode name="action">
- <properties>
- <help>Keep-alive failure action</help>
- <completionHelp>
- <list>hold clear restart</list>
- </completionHelp>
- <valueHelp>
- <format>hold</format>
- <description>Set action to hold (default)</description>
- </valueHelp>
- <valueHelp>
- <format>clear</format>
- <description>Set action to clear</description>
- </valueHelp>
- <valueHelp>
- <format>restart</format>
- <description>Set action to restart</description>
- </valueHelp>
- <constraint>
- <regex>^(hold|clear|restart)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="interval">
- <properties>
- <help>Keep-alive interval</help>
- <valueHelp>
- <format>u32:2-86400</format>
- <description>Keep-alive interval in seconds (default 30)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 2-86400"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="timeout">
- <properties>
- <help>Dead-Peer-Detection keep-alive timeout (IKEv1 only)</help>
- <valueHelp>
- <format>u32:2-86400</format>
- <description>Keep-alive timeout in seconds (default 120)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 2-86400"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="ikev2-reauth">
- <properties>
- <help>ikev2-reauth_help</help>
- <completionHelp>
- <list>yes no</list>
- </completionHelp>
- <valueHelp>
- <format>yes</format>
- <description>Enable remote host re-autentication during an IKE rekey. Currently broken due to a strong swan bug</description>
- </valueHelp>
- <valueHelp>
- <format>no</format>
- <description>Disable remote host re-authenticaton during an IKE rekey. (Default)</description>
- </valueHelp>
- <constraint>
- <regex>^(yes|no)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="key-exchange">
- <properties>
- <help>Key Exchange Version</help>
- <completionHelp>
- <list>ikev1 ikev2</list>
- </completionHelp>
- <valueHelp>
- <format>ikev1</format>
- <description>Use IKEv1 for Key Exchange [DEFAULT]</description>
- </valueHelp>
- <valueHelp>
- <format>ikev2</format>
- <description>Use IKEv2 for Key Exchange</description>
- </valueHelp>
- <constraint>
- <regex>^(ikev1|ikev2)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="lifetime">
- <properties>
- <help>IKE lifetime</help>
- <valueHelp>
- <format>u32:30-86400</format>
- <description>IKE lifetime in seconds (default 28800)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 30-86400"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="mobike">
- <properties>
- <help>Enable MOBIKE Support. MOBIKE is only available for IKEv2.</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable MOBIKE (default for IKEv2)</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable MOBIKE</description>
- </valueHelp>
- <constraint>
- <regex>^(enable|disable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="mode">
- <properties>
- <help>IKEv1 Phase 1 Mode Selection</help>
- <completionHelp>
- <list>main aggressive</list>
- </completionHelp>
- <valueHelp>
- <format>main</format>
- <description>Use Main mode for Key Exchanges in the IKEv1 Protocol (Recommended Default)</description>
- </valueHelp>
- <valueHelp>
- <format>aggressive</format>
- <description>Use Aggressive mode for Key Exchanges in the IKEv1 protocol - We do not recommend users to use aggressive mode as it is much more insecure compared to Main mode.</description>
- </valueHelp>
- <constraint>
- <regex>^(main|aggressive)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <tagNode name="proposal">
- <properties>
- <help>proposal_help</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>IKE-group proposal</description>
- </valueHelp>
- </properties>
- <children>
- <leafNode name="dh-group">
- <properties>
- <help>dh-grouphelp</help>
- <completionHelp>
- <list>1 2 5 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32</list>
- </completionHelp>
- <valueHelp>
- <format>1</format>
- <description>Diffie-Hellman group 1 (modp768)</description>
- </valueHelp>
- <valueHelp>
- <format>2</format>
- <description>Diffie-Hellman group 2 (modp1024)</description>
- </valueHelp>
- <valueHelp>
- <format>5</format>
- <description>Diffie-Hellman group 5 (modp1536)</description>
- </valueHelp>
- <valueHelp>
- <format>14</format>
- <description>Diffie-Hellman group 14 (modp2048)</description>
- </valueHelp>
- <valueHelp>
- <format>15</format>
- <description>Diffie-Hellman group 15 (modp3072)</description>
- </valueHelp>
- <valueHelp>
- <format>16</format>
- <description>Diffie-Hellman group 16 (modp4096)</description>
- </valueHelp>
- <valueHelp>
- <format>17</format>
- <description>Diffie-Hellman group 17 (modp6144)</description>
- </valueHelp>
- <valueHelp>
- <format>18</format>
- <description>Diffie-Hellman group 18 (modp8192)</description>
- </valueHelp>
- <valueHelp>
- <format>19</format>
- <description>Diffie-Hellman group 19 (ecp256)</description>
- </valueHelp>
- <valueHelp>
- <format>20</format>
- <description>Diffie-Hellman group 20 (ecp384)</description>
- </valueHelp>
- <valueHelp>
- <format>21</format>
- <description>Diffie-Hellman group 21 (ecp521)</description>
- </valueHelp>
- <valueHelp>
- <format>22</format>
- <description>Diffie-Hellman group 22 (modp1024s160)</description>
- </valueHelp>
- <valueHelp>
- <format>23</format>
- <description>Diffie-Hellman group 23 (modp2048s224)</description>
- </valueHelp>
- <valueHelp>
- <format>24</format>
- <description>Diffie-Hellman group 24 (modp2048s256)</description>
- </valueHelp>
- <valueHelp>
- <format>25</format>
- <description>Diffie-Hellman group 25 (ecp192)</description>
- </valueHelp>
- <valueHelp>
- <format>26</format>
- <description>Diffie-Hellman group 26 (ecp224)</description>
- </valueHelp>
- <valueHelp>
- <format>27</format>
- <description>Diffie-Hellman group 27 (ecp224bp)</description>
- </valueHelp>
- <valueHelp>
- <format>28</format>
- <description>Diffie-Hellman group 28 (ecp256bp)</description>
- </valueHelp>
- <valueHelp>
- <format>29</format>
- <description>Diffie-Hellman group 29 (ecp384bp)</description>
- </valueHelp>
- <valueHelp>
- <format>30</format>
- <description>Diffie-Hellman group 30 (ecp512bp)</description>
- </valueHelp>
- <valueHelp>
- <format>31</format>
- <description>Diffie-Hellman group 31 (curve25519)</description>
- </valueHelp>
- <valueHelp>
- <format>32</format>
- <description>Diffie-Hellman group 32 (curve448)</description>
- </valueHelp>
- <constraint>
- <regex>^(1|2|5|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32)$</regex>
- </constraint>
- </properties>
- </leafNode>
- #include <include/vpn-ipsec-encryption.xml.i>
- #include <include/vpn-ipsec-hash.xml.i>
- </children>
- </tagNode>
- </children>
- </tagNode>
- <leafNode name="include-ipsec-conf">
- <properties>
- <help>Sets to include an additional configuration directive file for strongSwan. Use an absolute path to specify the included file</help>
- </properties>
- </leafNode>
- <leafNode name="include-ipsec-secrets">
- <properties>
- <help>Sets to include an additional secrets file for strongSwan. Use an absolute path to specify the included file.</help>
- </properties>
- </leafNode>
- <node name="ipsec-interfaces">
- <properties>
- <help>Interface to use for VPN [REQUIRED]</help>
- </properties>
- <children>
- <leafNode name="interface">
- <properties>
- <help>IPsec interface [REQUIRED]</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="logging">
- <properties>
- <help>IPsec logging</help>
- </properties>
- <children>
- <leafNode name="log-level">
- <properties>
- <help>strongSwan Logger Level</help>
- <valueHelp>
- <format>u32:0-2</format>
- <description>Logger Verbosity Level (default 0)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-2"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="log-modes">
- <properties>
- <help>Log mode. To see what each log mode exactly does, please refer to the strongSwan documentation</help>
- <completionHelp>
- <list>dmn mgr ike chd job cfg knl net asn enc lib esp tls tnc imc imv pts any</list>
- </completionHelp>
- <valueHelp>
- <format>dmn</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>mgr</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>ike</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>chd</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>job</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>cfg</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>knl</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>net</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>asn</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>enc</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>lib</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>esp</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>tls</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>tnc</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>imc</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>imv</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>pts</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <valueHelp>
- <format>any</format>
- <description>Debug log option for strongSwan</description>
- </valueHelp>
- <constraint>
- <regex>^(dmn|mgr|ike|chd|job|cfg|knl|net|asn|enc|lib|esp|tls|tnc|imc|imv|pts|any)$</regex>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="nat-networks">
- <properties>
- <help>Network Address Translation (NAT) networks</help>
- </properties>
- <children>
- <tagNode name="allowed-network">
- <properties>
- <help>NAT networks to allow</help>
- <valueHelp>
- <format>ipv4net</format>
- <description>NAT networks to allow</description>
- </valueHelp>
- <constraint>
- <validator name="ip-prefix"/>
- </constraint>
- </properties>
- <children>
- <leafNode name="exclude">
- <properties>
- <help>NAT networks to exclude from allowed-networks</help>
- <valueHelp>
- <format>ipv4net</format>
- <description>NAT networks to exclude from allowed-networks</description>
- </valueHelp>
- <constraint>
- <validator name="ip-prefix"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- </children>
- </node>
- <leafNode name="nat-traversal">
- <properties>
- <help>Network Address Translation (NAT) traversal</help>
- <completionHelp>
- <list>disable enable</list>
- </completionHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable NAT-T</description>
- </valueHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable NAT-T</description>
- </valueHelp>
- <constraint>
- <regex>^(disable|enable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <node name="options">
- <properties>
- <help>Global IPsec settings</help>
- </properties>
- <children>
- <leafNode name="disable-route-autoinstall">
- <properties>
- <help>Do not automatically install routes to remote networks</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <tagNode name="profile">
- <properties>
- <help>VPN IPSec Profile</help>
- </properties>
- <children>
- <node name="authentication">
- <properties>
- <help>Authentication [REQUIRED]</help>
- </properties>
- <children>
- <node name="mode">
- <properties>
- <help>Authentication mode</help>
- </properties>
- <children>
- <leafNode name="pre-shared-secret">
- <properties>
- <help>Use pre-shared secret key</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="pre-shared-secret">
- <properties>
- <help>Pre-shared secret key</help>
- <valueHelp>
- <format>txt</format>
- <description>Pre-shared secret key</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="bind">
- <properties>
- <help>DMVPN crypto configuration</help>
- </properties>
- <children>
- <leafNode name="bind_child">
- <properties>
- <help>bind_child_help</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="esp-group">
- <properties>
- <help>Esp group name [REQUIRED]</help>
- <completionHelp>
- <path>vpn ipsec esp-group</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ike-group">
- <properties>
- <help>Ike group name [REQUIRED]</help>
- <completionHelp>
- <path>vpn ipsec ike-group</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- <node name="site-to-site">
- <properties>
- <help>Site to site VPN</help>
- </properties>
- <children>
- <tagNode name="peer">
- <properties>
- <help>VPN peer</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address of the peer</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of the peer</description>
- </valueHelp>
- <valueHelp>
- <format>txt</format>
- <description>Hostname of the peer</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;@text&gt;</format>
- <description>ID of the peer</description>
- </valueHelp>
- </properties>
- <children>
- <node name="authentication">
- <properties>
- <help>Peer authentication [REQUIRED]</help>
- </properties>
- <children>
- <leafNode name="id">
- <properties>
- <help>ID for peer authentication</help>
- <valueHelp>
- <format>txt</format>
- <description>ID used for peer authentication</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="mode">
- <properties>
- <help>Authentication mode</help>
- <completionHelp>
- <list>pre-shared-secret rsa x509</list>
- </completionHelp>
- <valueHelp>
- <format>pre-shared-secret</format>
- <description>pre-shared-secret_description</description>
- </valueHelp>
- <valueHelp>
- <format>rsa</format>
- <description>rsa_description</description>
- </valueHelp>
- <valueHelp>
- <format>x509</format>
- <description>x509_description</description>
- </valueHelp>
- <constraint>
- <regex>^(pre-shared-secret|rsa|x509)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="pre-shared-secret">
- <properties>
- <help>Pre-shared secret key</help>
- <valueHelp>
- <format>txt</format>
- <description>Pre-shared secret key</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="remote-id">
- <properties>
- <help>ID for remote authentication</help>
- <valueHelp>
- <format>txt</format>
- <description>ID used for peer authentication</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="rsa-key-name">
- <properties>
- <help>RSA key name</help>
- </properties>
- </leafNode>
- <leafNode name="use-x509-id">
- <properties>
- <help>Use certificate common name as ID</help>
- <valueless/>
- </properties>
- </leafNode>
- <node name="x509">
- <properties>
- <help>X.509 certificate</help>
- </properties>
- <children>
- #include <include/certificate.xml.i>
- #include <include/certificate-ca.xml.i>
- <leafNode name="crl-file">
- <properties>
- <help>File containing the X.509 Certificate Revocation List (CRL)</help>
- <valueHelp>
- <format>txt</format>
- <description>File in /config/auth</description>
- </valueHelp>
- </properties>
- </leafNode>
- <node name="key">
- <properties>
- <help>Key file and password to open it</help>
- </properties>
- <children>
- <leafNode name="file">
- <properties>
- <help>File containing the private key for the X.509 certificate for this host</help>
- <valueHelp>
- <format>txt</format>
- <description>File in /config/auth</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="password">
- <properties>
- <help>Password that protects the private key</help>
- <valueHelp>
- <format>txt</format>
- <description>Password that protects the private key</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
- <leafNode name="connection-type">
- <properties>
- <help>Connection type</help>
- <completionHelp>
- <list>initiate respond</list>
- </completionHelp>
- <valueHelp>
- <format>initiate</format>
- <description>initiate_description</description>
- </valueHelp>
- <valueHelp>
- <format>respond</format>
- <description>respond_description</description>
- </valueHelp>
- <constraint>
- <regex>^(initiate|respond)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="default-esp-group">
- <properties>
- <help>Defult ESP group name</help>
- </properties>
- </leafNode>
- <leafNode name="description">
- <properties>
- <help>VPN peer description</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="dhcp-interface">
- <properties>
- <help>DHCP interface to listen on</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="force-encapsulation">
- <properties>
- <help>Force UDP Encapsulation for ESP Payloads</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>This endpoint will force UDP encapsulation for this peer</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>This endpoint will not force UDP encapsulation for this peer</description>
- </valueHelp>
- <constraint>
- <regex>^(enable|disable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="ike-group">
- <properties>
- <help>Internet Key Exchange (IKE) group name [REQUIRED]</help>
- <completionHelp>
- <path>vpn ipsec ike-group</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ikev2-reauth">
- <properties>
- <help>Re-authentication of the remote peer during an IKE re-key. IKEv2 option only</help>
- <completionHelp>
- <list>yes no inherit</list>
- </completionHelp>
- <valueHelp>
- <format>yes</format>
- <description>Enable remote host re-autentication during an IKE re-key. Currently broken due to a strong swan bug</description>
- </valueHelp>
- <valueHelp>
- <format>no</format>
- <description>Disable remote host re-authenticaton during an IKE re-key.</description>
- </valueHelp>
- <valueHelp>
- <format>inherit</format>
- <description>Inherit the reauth configuration form your IKE-group (Default)</description>
- </valueHelp>
- <constraint>
- <regex>^(yes|no|inherit)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="local-address">
- <properties>
- <help>IPv4 or IPv6 address of a local interface to use for VPN</help>
- <completionHelp>
- <list>any</list>
- </completionHelp>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address of a local interface for VPN</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of a local interface for VPN</description>
- </valueHelp>
- <valueHelp>
- <format>any</format>
- <description>Allow any IPv4 address present on the system to be used for VPN</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- <regex>^(any)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <tagNode name="tunnel">
- <properties>
- <help>Peer tunnel [REQUIRED]</help>
- <valueHelp>
- <format>u32</format>
- <description>Peer tunnel [REQUIRED]</description>
- </valueHelp>
- </properties>
- <children>
- <leafNode name="allow-nat-networks">
- <properties>
- <help>Option to allow NAT networks</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable NAT networks</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable NAT networks (default)</description>
- </valueHelp>
- <constraint>
- <regex>^(enable|disable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="allow-public-networks">
- <properties>
- <help>Option to allow public networks</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable public networks</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable public networks (default)</description>
- </valueHelp>
- <constraint>
- <regex>^(enable|disable)$</regex>
- </constraint>
- </properties>
- </leafNode>
- #include <include/generic-disable-node.xml.i>
- <leafNode name="esp-group">
- <properties>
- <help>ESP group name</help>
- <completionHelp>
- <path>vpn ipsec esp-group</path>
- </completionHelp>
- </properties>
- </leafNode>
- <node name="local">
- <properties>
- <help>Local parameters for interesting traffic</help>
- </properties>
- <children>
- <leafNode name="port">
- <properties>
- <help>Any TCP or UDP port</help>
- <valueHelp>
- <format>port name</format>
- <description>Named port (any name in /etc/services, e.g., http)</description>
- </valueHelp>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numbered port</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="prefix">
- <properties>
- <help>Local IPv4 or IPv6 prefix</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Local IPv4 prefix</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>Local IPv6 prefix</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-prefix"/>
- <validator name="ipv6-prefix"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="protocol">
- <properties>
- <help>Protocol to encrypt</help>
- <valueless/>
- </properties>
- </leafNode>
- <node name="remote">
- <properties>
- <help>Remote parameters for interesting traffic</help>
- </properties>
- <children>
- <leafNode name="port">
- <properties>
- <help>Any TCP or UDP port</help>
- <valueHelp>
- <format>port name</format>
- <description>Named port (any name in /etc/services, e.g., http)</description>
- </valueHelp>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numbered port</description>
- </valueHelp>
- </properties>
- </leafNode>
- <leafNode name="prefix">
- <properties>
- <help>Remote IPv4 or IPv6 prefix</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Remote IPv4 prefix</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>Remote IPv6 prefix</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-prefix"/>
- <validator name="ipv6-prefix"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
- <node name="vti">
- <properties>
- <help>Virtual tunnel interface [REQUIRED]</help>
- </properties>
- <children>
- <leafNode name="bind">
- <properties>
- <help>VTI tunnel interface associated with this configuration [REQUIRED]</help>
- </properties>
- </leafNode>
- <leafNode name="esp-group">
- <properties>
- <help>ESP group name [REQUIRED]</help>
- <completionHelp>
- <path>vpn ipsec esp-group</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in
index 5bdebcb05..ff3219866 100644
--- a/interface-definitions/vpn_l2tp.xml.in
+++ b/interface-definitions/vpn_l2tp.xml.in
@@ -22,7 +22,7 @@
</properties>
</leafNode>
#include <include/accel-ppp/gateway-address.xml.i>
- #include <include/accel-ppp/name-server.xml.i>
+ #include <include/name-server-ipv4-ipv6.xml.i>
<node name="lns">
<properties>
<help>L2TP Network Server (LNS)</help>
@@ -33,6 +33,11 @@
<help>Tunnel password used to authenticate the client (LAC)</help>
</properties>
</leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>Sent to the client (LAC) in the Host-Name attribute</help>
+ </properties>
+ </leafNode>
</children>
</node>
<leafNode name="ccp-disable">
@@ -220,9 +225,9 @@
#include <include/accel-ppp/radius-additions-disable-accounting.xml.i>
<leafNode name="fail-time">
<properties>
- <help>Mark server unavailable for &lt;n&gt; seconds on failure</help>
+ <help>Mark server unavailable for N seconds on failure</help>
<valueHelp>
- <format>0-600</format>
+ <format>u32:0-600</format>
<description>Fail time penalty</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in
index 1a9d39a12..f35b1ebbd 100644
--- a/interface-definitions/vpn_openconnect.xml.in
+++ b/interface-definitions/vpn_openconnect.xml.in
@@ -75,7 +75,7 @@
</node>
<node name="listen-ports">
<properties>
- <help>SSL Certificate, SSL Key and CA (/config/auth)</help>
+ <help>Specify custom ports to use for client connections</help>
</properties>
<children>
<leafNode name="tcp">
@@ -190,7 +190,7 @@
</leafNode>
</children>
</node>
- #include <include/accel-ppp/name-server.xml.i>
+ #include <include/name-server-ipv4-ipv6.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/vpn_pptp.xml.in b/interface-definitions/vpn_pptp.xml.in
index 91c8cd76f..9b84a00c1 100644
--- a/interface-definitions/vpn_pptp.xml.in
+++ b/interface-definitions/vpn_pptp.xml.in
@@ -21,19 +21,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="name-server">
- <properties>
- <help>Domain Name Server (DNS) propagated to client</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Domain Name Server (DNS) IPv4 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/name-server-ipv4.xml.i>
#include <include/accel-ppp/wins-server.xml.i>
<node name="client-ip-pool">
<properties>
diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in
index e4ade844d..ad905a1f0 100644
--- a/interface-definitions/vpn_sstp.xml.in
+++ b/interface-definitions/vpn_sstp.xml.in
@@ -25,9 +25,9 @@
</node>
</children>
</node>
- #include <include/interface/interface-mtu-68-1500.xml.i>
+ #include <include/interface/mtu-68-1500.xml.i>
#include <include/accel-ppp/gateway-address.xml.i>
- #include <include/accel-ppp/name-server.xml.i>
+ #include <include/name-server-ipv4-ipv6.xml.i>
<node name="client-ip-pool">
<properties>
<help>Client IP pools and gateway setting</help>
@@ -43,6 +43,8 @@
</properties>
<children>
#include <include/accel-ppp/ppp-mppe.xml.i>
+ #include <include/accel-ppp/ppp-options-ipv4.xml.i>
+ #include <include/accel-ppp/ppp-options-ipv6.xml.i>
#include <include/accel-ppp/lcp-echo-interval-failure.xml.i>
#include <include/accel-ppp/lcp-echo-timeout.xml.i>
</children>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 5fd758a44..306b15d60 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -30,17 +30,17 @@
<properties>
<help>Routing table associated with this instance</help>
<valueHelp>
- <format>100-2147483647</format>
+ <format>u32:100-65535</format>
<description>Routing table ID</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 100-2147483647"/>
+ <validator name="numeric" argument="--range 100-65535"/>
</constraint>
- <constraintErrorMessage>VRF routing table must be in range from 100 to 2147483647</constraintErrorMessage>
+ <constraintErrorMessage>VRF routing table must be in range from 100 to 65535</constraintErrorMessage>
</properties>
</leafNode>
- #include <include/interface/interface-description.xml.i>
- #include <include/interface/interface-disable.xml.i>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in
index 54cd44275..b58cf735c 100644
--- a/interface-definitions/vrrp.xml.in
+++ b/interface-definitions/vrrp.xml.in
@@ -35,6 +35,7 @@
<validator name="numeric" argument="--range 1-255"/>
</constraint>
</properties>
+ <defaultValue>1</defaultValue>
</leafNode>
<node name="authentication">
<properties>
@@ -45,7 +46,7 @@
<properties>
<help>VRRP password</help>
<valueHelp>
- <format>text</format>
+ <format>txt</format>
<description>Password string (up to 8 characters)</description>
</valueHelp>
<constraint>
@@ -60,6 +61,14 @@
<completionHelp>
<list>plaintext-password ah</list>
</completionHelp>
+ <valueHelp>
+ <format>plaintext-password</format>
+ <description>Simple password string</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ah</format>
+ <description>AH - IPSEC (not recommended)</description>
+ </valueHelp>
<constraint>
<regex>^(plaintext-password|ah)$</regex>
</constraint>
@@ -86,6 +95,7 @@
<validator name="numeric" argument="--positive" />
</constraint>
</properties>
+ <defaultValue>3</defaultValue>
</leafNode>
<leafNode name="interval">
<properties>
@@ -94,6 +104,7 @@
<validator name="numeric" argument="--positive"/>
</constraint>
</properties>
+ <defaultValue>60</defaultValue>
</leafNode>
<leafNode name="script">
<properties>
@@ -156,6 +167,7 @@
<validator name="numeric" argument="--range 0-1000"/>
</constraint>
</properties>
+ <defaultValue>0</defaultValue>
</leafNode>
<leafNode name="priority">
<properties>
@@ -168,6 +180,7 @@
<validator name="numeric" argument="--range 1-255"/>
</constraint>
</properties>
+ <defaultValue>100</defaultValue>
</leafNode>
<leafNode name="rfc3768-compatibility">
<properties>
@@ -282,7 +295,7 @@
<multi/>
<help>Sync group member</help>
<valueHelp>
- <format>text</format>
+ <format>txt</format>
<description>VRRP group name</description>
</valueHelp>
<completionHelp>
diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in
index 1dacbd5ba..241cca0ce 100644
--- a/op-mode-definitions/dhcp.xml.in
+++ b/op-mode-definitions/dhcp.xml.in
@@ -22,7 +22,7 @@
<properties>
<help>Show DHCP server leases for a specific pool</help>
<completionHelp>
- <script>sudo ${vyos_op_scripts_dir}/show_dhcp.py --allowed pool</script>
+ <path>service dhcp-server shared-network-name</path>
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/show_dhcp.py --leases --pool $6</command>
@@ -57,7 +57,7 @@
<properties>
<help>Show DHCP server statistics for a specific pool</help>
<completionHelp>
- <script>sudo ${vyos_op_scripts_dir}/show_dhcp.py --allowed pool</script>
+ <path>service dhcp-server shared-network-name</path>
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/show_dhcp.py --statistics --pool $6</command>
@@ -123,18 +123,18 @@
<children>
<node name="dhcp">
<properties>
- <help>Restart DHCP server processes</help>
+ <help>Restart DHCP processes</help>
</properties>
<children>
<node name="server">
<properties>
- <help>Restart the DHCP server process</help>
+ <help>Restart DHCP server</help>
</properties>
- <command>sudo systemctl restart isc-dhcp-server.service</command>
+ <command>if cli-shell-api existsActive service dhcp-server; then sudo systemctl restart isc-dhcp-server.service; else echo "DHCP server not configured"; fi</command>
</node>
<node name="relay-agent">
<properties>
- <help>Restart the DHCP server process</help>
+ <help>Restart DHCP relay-agent</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_dhcp_relay.py --ipv4</command>
</node>
@@ -142,18 +142,18 @@
</node>
<node name="dhcpv6">
<properties>
- <help>Restart DHCPv6 server processes</help>
+ <help>Restart DHCPv6 processes</help>
</properties>
<children>
<node name="server">
<properties>
- <help>Restart the DHCPv6 server process</help>
+ <help>Restart DHCPv6 server</help>
</properties>
- <command>sudo systemctl restart isc-dhcp-server6.service</command>
+ <command>if cli-shell-api existsActive service dhcpv6-server; then sudo systemctl restart isc-dhcp-server6.service; else echo "DHCPv6 server not configured"; fi</command>
</node>
<node name="relay-agent">
<properties>
- <help>Restart the DHCP server process</help>
+ <help>Restart DHCPv6 relay-agent</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_dhcp_relay.py --ipv6</command>
</node>
diff --git a/op-mode-definitions/dns-forwarding.xml.in b/op-mode-definitions/dns-forwarding.xml.in
index 36fe6b5ef..6574f2319 100644
--- a/op-mode-definitions/dns-forwarding.xml.in
+++ b/op-mode-definitions/dns-forwarding.xml.in
@@ -59,9 +59,6 @@
</children>
</node>
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="dns">
<properties>
diff --git a/op-mode-definitions/generate-ipsec-debug-archive.xml.in b/op-mode-definitions/generate-ipsec-debug-archive.xml.in
new file mode 100644
index 000000000..ecd7108c4
--- /dev/null
+++ b/op-mode-definitions/generate-ipsec-debug-archive.xml.in
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="generate">
+ <children>
+ <node name="ipsec">
+ <properties>
+ <help>Generate IPsec archives</help>
+ </properties>
+ <children>
+ <node name="debug-archive">
+ <properties>
+ <help>Generate IPSec debug-archive</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/generate_ipsec_debug_archive.sh</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i
index 7fc59f3b0..4d5f56656 100644
--- a/op-mode-definitions/include/bgp/afi-common.xml.i
+++ b/op-mode-definitions/include/bgp/afi-common.xml.i
@@ -7,23 +7,33 @@
</completionHelp>
</properties>
<children>
- <leafNode name="exact-match">
- <properties>
- <help>Exact match of the communities</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </leafNode>
+ #include <include/bgp/exact-match.xml.i>
</children>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</tagNode>
<tagNode name="large-community">
<properties>
- <help>List of large-community numbers</help>
+ <help>Display routes matching the large-communities</help>
<completionHelp>
<list>AA:BB:CC</list>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/bgp/exact-match.xml.i>
+ </children>
+</tagNode>
+<tagNode name="large-community-list">
+ <properties>
+ <help>Display routes matching the large-community-list</help>
+ <completionHelp>
+ <path>policy large-community-list</path>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/bgp/exact-match.xml.i>
+ </children>
</tagNode>
<leafNode name="statistics">
<properties>
diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i
index f1b699347..a51595b7f 100644
--- a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i
+++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i
@@ -22,12 +22,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</leafNode>
- <leafNode name="exact-match">
- <properties>
- <help>Exact match of the communities</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </leafNode>
+ #include <include/bgp/exact-match.xml.i>
<leafNode name="graceful-shutdown">
<properties>
<help>Graceful shutdown (well-known community)</help>
@@ -105,12 +100,7 @@
</completionHelp>
</properties>
<children>
- <leafNode name="exact-match">
- <properties>
- <help>Show BGP routes exactly matching specified community list</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </leafNode>
+ #include <include/bgp/exact-match.xml.i>
</children>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</tagNode>
diff --git a/op-mode-definitions/include/bgp/exact-match.xml.i b/op-mode-definitions/include/bgp/exact-match.xml.i
new file mode 100644
index 000000000..49026db9b
--- /dev/null
+++ b/op-mode-definitions/include/bgp/exact-match.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from bgp/exact-match.xml.i -->
+<leafNode name="exact-match">
+ <properties>
+ <help>Exact match of the communities</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i
index 785b85963..4f112905e 100644
--- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i
+++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i
@@ -21,6 +21,7 @@
#include <include/bgp/afi-common.xml.i>
#include <include/bgp/afi-ipv4-ipv6-common.xml.i>
</children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</node>
<tagNode name="ipv6">
<properties>
@@ -42,5 +43,6 @@
#include <include/bgp/afi-common.xml.i>
#include <include/bgp/afi-ipv4-ipv6-common.xml.i>
</children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</node>
<!-- included end -->
diff --git a/op-mode-definitions/ipv4-route.xml.in b/op-mode-definitions/ipv4-route.xml.in
index aab3df0f1..8f001d5bb 100644
--- a/op-mode-definitions/ipv4-route.xml.in
+++ b/op-mode-definitions/ipv4-route.xml.in
@@ -20,11 +20,7 @@
</node>
</children>
</node>
-
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="ip">
<properties>
@@ -56,7 +52,6 @@
</tagNode>
</children>
</node>
-
<node name="route">
<properties>
<help>Reset IP route</help>
@@ -68,7 +63,6 @@
</properties>
<command>sudo ip route flush cache</command>
</leafNode>
-
<tagNode name="cache">
<properties>
<help>Flush the kernel route cache for a given route</help>
diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in
index 28f5b1aad..1c238e1f3 100644
--- a/op-mode-definitions/ipv6-route.xml.in
+++ b/op-mode-definitions/ipv6-route.xml.in
@@ -28,11 +28,7 @@
</node>
</children>
</node>
-
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="ipv6">
<properties>
@@ -64,7 +60,6 @@
</tagNode>
</children>
</node>
-
<node name="route">
<properties>
<help>Reset IPv6 route</help>
@@ -76,7 +71,6 @@
</properties>
<command>sudo ip -f inet6 route flush cache</command>
</leafNode>
-
<tagNode name="cache">
<properties>
<help>Flush the kernel IPv6 route cache for a given route</help>
diff --git a/op-mode-definitions/monitor-protocol.xml.in b/op-mode-definitions/monitor-protocol.xml.in
index 6a6bd50f3..f3af3575c 100644
--- a/op-mode-definitions/monitor-protocol.xml.in
+++ b/op-mode-definitions/monitor-protocol.xml.in
@@ -263,13 +263,14 @@
</node>
<node name="ospf">
<properties>
- <help>Monitor the Open Shortest Path First (OSPF) protocol</help>
+ <help>Monitor Open Shortest Path First (OSPF) protocol</help>
</properties>
<children>
#include <include/monitor-background.xml.i>
-
-
<node name="disable">
+ <properties>
+ <help>Disable Open Shortest Path First (OSPF) debugging</help>
+ </properties>
<children>
<node name="event">
<properties>
@@ -458,6 +459,9 @@
</children>
</node>
<node name="enable">
+ <properties>
+ <help>Enable Open Shortest Path First (OSPF) debugging</help>
+ </properties>
<children>
<node name="event">
<properties>
diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in
index f8dc0cff0..0a8cf64d7 100644
--- a/op-mode-definitions/openvpn.xml.in
+++ b/op-mode-definitions/openvpn.xml.in
@@ -45,11 +45,11 @@
</children>
</node>
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="openvpn">
+ <properties>
+ <help>Reset OpenVPN client/server connections</help>
+ </properties>
<children>
<tagNode name="client">
<properties>
diff --git a/op-mode-definitions/pppoe-server.xml.in b/op-mode-definitions/pppoe-server.xml.in
index 6d89b3e77..3d0df44a3 100644
--- a/op-mode-definitions/pppoe-server.xml.in
+++ b/op-mode-definitions/pppoe-server.xml.in
@@ -40,9 +40,6 @@
</children>
</node>
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="pppoe-server">
<properties>
diff --git a/op-mode-definitions/reset-conntrack.xml.in b/op-mode-definitions/reset-conntrack.xml.in
index 827ba4af4..9c8265f77 100644
--- a/op-mode-definitions/reset-conntrack.xml.in
+++ b/op-mode-definitions/reset-conntrack.xml.in
@@ -1,9 +1,6 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="conntrack">
<properties>
diff --git a/op-mode-definitions/reset-vpn.xml.in b/op-mode-definitions/reset-vpn.xml.in
index 71dbb4ed9..94ee1c7df 100644
--- a/op-mode-definitions/reset-vpn.xml.in
+++ b/op-mode-definitions/reset-vpn.xml.in
@@ -1,9 +1,6 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="reset">
- <properties>
- <help>Reset a service</help>
- </properties>
<children>
<node name="vpn">
<properties>
diff --git a/op-mode-definitions/restart-frr.xml.in b/op-mode-definitions/restart-frr.xml.in
index 96ad1a650..475bd1ee8 100644
--- a/op-mode-definitions/restart-frr.xml.in
+++ b/op-mode-definitions/restart-frr.xml.in
@@ -2,62 +2,66 @@
<interfaceDefinition>
<node name="restart">
<children>
- <node name="frr">
+ <leafNode name="all">
<properties>
- <help>Restart FRRouting daemons</help>
+ <help>Restart all routing daemons</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart</command>
- <children>
- <leafNode name="bfdd">
- <properties>
- <help>Restart Bidirectional Forwarding Detection daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd</command>
- </leafNode>
- <leafNode name="bgpd">
- <properties>
- <help>Restart Border Gateway Protocol daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd</command>
- </leafNode>
- <leafNode name="ospfd">
- <properties>
- <help>Restart OSPFv2 daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospfd</command>
- </leafNode>
- <leafNode name="ospf6d">
- <properties>
- <help>Restart OSPFv3 daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospf6d</command>
- </leafNode>
- <leafNode name="ripd">
- <properties>
- <help>Restart Routing Information Protocol daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripd</command>
- </leafNode>
- <leafNode name="ripngd">
- <properties>
- <help>Restart RIPng daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd</command>
- </leafNode>
- <leafNode name="staticd">
- <properties>
- <help>Restart Static Route daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd</command>
- </leafNode>
- <leafNode name="zebra">
- <properties>
- <help>Restart IP routing manager daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra</command>
- </leafNode>
- </children>
- </node>
+ </leafNode>
+ <leafNode name="bfd">
+ <properties>
+ <help>Restart Bidirectional Forwarding Detection (BFD) daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd</command>
+ </leafNode>
+ <leafNode name="bgp">
+ <properties>
+ <help>Restart Border Gateway Protocol (BGP) routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd</command>
+ </leafNode>
+ <leafNode name="isis">
+ <properties>
+ <help>Restart Intermediate System to Intermediate System (IS-IS) routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon isisd</command>
+ </leafNode>
+ <leafNode name="ospf">
+ <properties>
+ <help>Restart Open Shortest Path First (OSPF) routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospfd</command>
+ </leafNode>
+ <leafNode name="ospfv3">
+ <properties>
+ <help>Restart IPv6 Open Shortest Path First (OSPFv3) routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospf6d</command>
+ </leafNode>
+ <leafNode name="rip">
+ <properties>
+ <help>Restart Routing Information Protocol (RIP) routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripd</command>
+ </leafNode>
+ <leafNode name="ripng">
+ <properties>
+ <help>Restart Routing Information Protocol NG (RIPng) routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd</command>
+ </leafNode>
+ <leafNode name="static">
+ <properties>
+ <help>Restart static routing daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd</command>
+ </leafNode>
+ <leafNode name="zebra">
+ <properties>
+ <help>Restart Routing Information Base (RIB) manager daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra</command>
+ </leafNode>
</children>
</node>
</interfaceDefinition>
diff --git a/op-mode-definitions/show-interfaces-bonding.xml.in b/op-mode-definitions/show-interfaces-bonding.xml.in
index c1c76b059..d4e737d5b 100644
--- a/op-mode-definitions/show-interfaces-bonding.xml.in
+++ b/op-mode-definitions/show-interfaces-bonding.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="bonding">
<properties>
- <help>Show bonding interface information</help>
+ <help>Show specified Bonding interface information</help>
<completionHelp>
<path>interfaces bonding</path>
</completionHelp>
@@ -46,7 +46,7 @@
</tagNode>
<node name="bonding">
<properties>
- <help>Show bonding interface information</help>
+ <help>Show Bonding interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bonding --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-bridge.xml.in b/op-mode-definitions/show-interfaces-bridge.xml.in
index cc4b248b6..d4908b341 100644
--- a/op-mode-definitions/show-interfaces-bridge.xml.in
+++ b/op-mode-definitions/show-interfaces-bridge.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="bridge">
<properties>
- <help>Show bridge interface information</help>
+ <help>Show specified Bridge interface information</help>
<completionHelp>
<path>interfaces bridge</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="bridge">
<properties>
- <help>Show bridge interface information</help>
+ <help>Show Bridge interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bridge --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-dummy.xml.in b/op-mode-definitions/show-interfaces-dummy.xml.in
index 7c24c6921..52d2cc7ee 100644
--- a/op-mode-definitions/show-interfaces-dummy.xml.in
+++ b/op-mode-definitions/show-interfaces-dummy.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="dummy">
<properties>
- <help>Show dummy interface information</help>
+ <help>Show specified Dummy interface information</help>
<completionHelp>
<path>interfaces dummy</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="dummy">
<properties>
- <help>Show dummy interface information</help>
+ <help>Show Dummy interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=dummy --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in
index bdcfa55f1..e414291d1 100644
--- a/op-mode-definitions/show-interfaces-ethernet.xml.in
+++ b/op-mode-definitions/show-interfaces-ethernet.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="ethernet">
<properties>
- <help>Show ethernet interface information</help>
+ <help>Show specified Ethernet interface information</help>
<completionHelp>
<path>interfaces ethernet</path>
</completionHelp>
@@ -23,19 +23,19 @@
<properties>
<help>Visually identify specified ethernet interface</help>
</properties>
- <command>echo "Blinking interface $4 for 30 seconds."; /sbin/ethtool --identify "$4" 30</command>
+ <command>echo "Blinking interface $4 for 30 seconds."; ethtool --identify "$4" 30</command>
</leafNode>
<node name="physical">
<properties>
<help>Show physical device information for specified ethernet interface</help>
</properties>
- <command>/sbin/ethtool "$4"; /sbin/ethtool -i "$4"</command>
+ <command>ethtool "$4"; ethtool --show-ring "$4"; ethtool --driver "$4"</command>
<children>
<leafNode name="offload">
<properties>
<help>Show physical device offloading capabilities</help>
</properties>
- <command>/sbin/ethtool -k "$4" | sed -e 1d -e '/fixed/d' -e 's/^\t*//g' -e 's/://' | column -t -s' '</command>
+ <command>ethtool --show-features "$4" | sed -e 1d -e '/fixed/d' -e 's/^\t*//g' -e 's/://' | column -t -s' '</command>
</leafNode>
</children>
</node>
@@ -43,13 +43,13 @@
<properties>
<help>Show physical device statistics for specified ethernet interface</help>
</properties>
- <command>/sbin/ethtool -S "$4"</command>
+ <command>ethtool --statistics "$4"</command>
</leafNode>
<leafNode name="transceiver">
<properties>
<help>Show transceiver information from modules (e.g SFP+, QSFP)</help>
</properties>
- <command>/sbin/ethtool -m "$4"</command>
+ <command>ethtool --module-info "$4"</command>
</leafNode>
<tagNode name="vif">
<properties>
@@ -72,7 +72,7 @@
</tagNode>
<node name="ethernet">
<properties>
- <help>Show ethernet interface information</help>
+ <help>Show Ethernet interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=ethernet --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-input.xml.in b/op-mode-definitions/show-interfaces-input.xml.in
index 15e8203e5..9ae3828c8 100644
--- a/op-mode-definitions/show-interfaces-input.xml.in
+++ b/op-mode-definitions/show-interfaces-input.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="input">
<properties>
- <help>Show input interface information</help>
+ <help>Show specified Input interface information</help>
<completionHelp>
<path>interfaces input</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="input">
<properties>
- <help>Show input interface information</help>
+ <help>Show Input (ifb) interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=input --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-l2tpv3.xml.in b/op-mode-definitions/show-interfaces-l2tpv3.xml.in
index 60fee34a1..2a1d6a1c6 100644
--- a/op-mode-definitions/show-interfaces-l2tpv3.xml.in
+++ b/op-mode-definitions/show-interfaces-l2tpv3.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="l2tpv3">
<properties>
- <help>Show L2TPv3 interface information</help>
+ <help>Show specified L2TPv3 interface information</help>
<completionHelp>
<path>interfaces l2tpv3</path>
</completionHelp>
diff --git a/op-mode-definitions/show-interfaces-loopback.xml.in b/op-mode-definitions/show-interfaces-loopback.xml.in
index b30b57909..25a75ffff 100644
--- a/op-mode-definitions/show-interfaces-loopback.xml.in
+++ b/op-mode-definitions/show-interfaces-loopback.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="loopback">
<properties>
- <help>Show loopback interface information</help>
+ <help>Show specified Loopback interface information</help>
<completionHelp>
<path>interfaces loopback</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="loopback">
<properties>
- <help>Show loopback interface information</help>
+ <help>Show Loopback interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=loopback --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-pppoe.xml.in b/op-mode-definitions/show-interfaces-pppoe.xml.in
index 18697a275..767836abf 100644
--- a/op-mode-definitions/show-interfaces-pppoe.xml.in
+++ b/op-mode-definitions/show-interfaces-pppoe.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="pppoe">
<properties>
- <help>Show PPPoE interface information</help>
+ <help>Show specified PPPoE interface information</help>
<completionHelp>
<path>interfaces pppoe</path>
</completionHelp>
diff --git a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in
index 195944745..2ae4b5a9e 100644
--- a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in
+++ b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="pseudo-ethernet">
<properties>
- <help>Show pseudo-ethernet/MACvlan interface information</help>
+ <help>Show specified Pseudo-Ethernet/MACvlan interface information</help>
<completionHelp>
<path>interfaces pseudo-ethernet</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="pseudo-ethernet">
<properties>
- <help>Show pseudo-ethernet/MACvlan interface information</help>
+ <help>Show Pseudo-Ethernet/MACvlan interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=pseudo-ethernet --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-tunnel.xml.in b/op-mode-definitions/show-interfaces-tunnel.xml.in
index 416de0299..51b25efd9 100644
--- a/op-mode-definitions/show-interfaces-tunnel.xml.in
+++ b/op-mode-definitions/show-interfaces-tunnel.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="tunnel">
<properties>
- <help>Show tunnel interface information</help>
+ <help>Show specified Tunnel interface information</help>
<completionHelp>
<path>interfaces tunnel</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="tunnel">
<properties>
- <help>Show tunnel interface information</help>
+ <help>Show Tunnel interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=tunnel --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-vti.xml.in b/op-mode-definitions/show-interfaces-vti.xml.in
index f51be2d19..b436b8414 100644
--- a/op-mode-definitions/show-interfaces-vti.xml.in
+++ b/op-mode-definitions/show-interfaces-vti.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="vti">
<properties>
- <help>Show vti interface information</help>
+ <help>Show specified VTI interface information</help>
<completionHelp>
<path>interfaces vti</path>
</completionHelp>
@@ -23,7 +23,7 @@
</tagNode>
<node name="vti">
<properties>
- <help>Show vti interface information</help>
+ <help>Show VTI interface information</help>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=vti --action=show-brief</command>
<children>
diff --git a/op-mode-definitions/show-interfaces-vxlan.xml.in b/op-mode-definitions/show-interfaces-vxlan.xml.in
index 4e3cb93cd..1befd428c 100644
--- a/op-mode-definitions/show-interfaces-vxlan.xml.in
+++ b/op-mode-definitions/show-interfaces-vxlan.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="vxlan">
<properties>
- <help>Show VXLAN interface information</help>
+ <help>Show specified VXLAN interface information</help>
<completionHelp>
<path>interfaces vxlan</path>
</completionHelp>
diff --git a/op-mode-definitions/show-interfaces-wireguard.xml.in b/op-mode-definitions/show-interfaces-wireguard.xml.in
new file mode 100644
index 000000000..c9b754dcd
--- /dev/null
+++ b/op-mode-definitions/show-interfaces-wireguard.xml.in
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="interfaces">
+ <children>
+ <tagNode name="wireguard">
+ <properties>
+ <help>Show specified WireGuard interface information</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --type wireguard</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4"</command>
+ <children>
+ <leafNode name="allowed-ips">
+ <properties>
+ <help>Show all IP addresses allowed for the specified interface</help>
+ </properties>
+ <command>sudo wg show "$4" allowed-ips</command>
+ </leafNode>
+ <leafNode name="endpoints">
+ <properties>
+ <help>Show all endpoints for the specified interface</help>
+ </properties>
+ <command>sudo wg show "$4" endpoints</command>
+ </leafNode>
+ <leafNode name="peers">
+ <properties>
+ <help>Show all peer IDs for the specified interface</help>
+ </properties>
+ <command>sudo wg show "$4" peers</command>
+ </leafNode>
+ <leafNode name="public-key">
+ <properties>
+ <help>Show interface public-key</help>
+ </properties>
+ <command>sudo wg show "$4" public-key</command>
+ </leafNode>
+ <leafNode name="summary">
+ <properties>
+ <help>Shows current configuration and device information</help>
+ </properties>
+ <command>sudo wg show "$4"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="wireguard">
+ <properties>
+ <help>Show WireGuard interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show-brief</command>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed Wireguard interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show</command>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-interfaces-wireless.xml.in b/op-mode-definitions/show-interfaces-wireless.xml.in
new file mode 100644
index 000000000..4a37417aa
--- /dev/null
+++ b/op-mode-definitions/show-interfaces-wireless.xml.in
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="interfaces">
+ <children>
+ <node name="wireless">
+ <properties>
+ <help>Show Wireless (WLAN) interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show-brief</command>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed wireless interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show</command>
+ </leafNode>
+ <leafNode name="info">
+ <properties>
+ <help>Show wireless interface configuration</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_wireless.py --brief</command>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="wireless">
+ <properties>
+ <help>Show specified wireless interface information</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --type wireless</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4"</command>
+ <children>
+ <leafNode name="brief">
+ <properties>
+ <help>Show summary of the specified wireless interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" --action=show-brief</command>
+ </leafNode>
+ <node name="scan">
+ <properties>
+ <help>Show summary of the specified wireless interface information</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/show_wireless.py --scan "$4"</command>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show detailed scan results</help>
+ </properties>
+ <command>sudo /sbin/iw dev "$4" scan ap-force</command>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="stations">
+ <properties>
+ <help>Show specified Wireless interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_wireless.py --stations "$4"</command>
+ </leafNode>
+ <tagNode name="vif">
+ <properties>
+ <help>Show specified virtual network interface (vif) information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6"</command>
+ <children>
+ <leafNode name="brief">
+ <properties>
+ <help>Show summary of specified virtual network interface (vif) information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6" --action=show-brief</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-interfaces-wwan.xml.in b/op-mode-definitions/show-interfaces-wwan.xml.in
index d57e17a13..3cd29b38a 100644
--- a/op-mode-definitions/show-interfaces-wwan.xml.in
+++ b/op-mode-definitions/show-interfaces-wwan.xml.in
@@ -6,7 +6,7 @@
<children>
<tagNode name="wwan">
<properties>
- <help>Show Wireless Wire Area Network (WWAN) interface information</help>
+ <help>Show specified Wireless Wire Area Network (WWAN) interface information</help>
<completionHelp>
<path>interfaces wwan</path>
<script>cd /sys/class/net; ls -d wwan*</script>
@@ -68,9 +68,9 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --sim</command>
</leafNode>
- <leafNode name="summary">
+ <leafNode name="detail">
<properties>
- <help>Show WWAN module information summary</help>
+ <help>Show WWAN module detailed information summary</help>
</properties>
<command>mmcli --modem ${4#wwan}</command>
</leafNode>
diff --git a/op-mode-definitions/show-ip-route.xml.in b/op-mode-definitions/show-ip-route.xml.in
index 729572b4a..740993693 100644
--- a/op-mode-definitions/show-ip-route.xml.in
+++ b/op-mode-definitions/show-ip-route.xml.in
@@ -125,16 +125,11 @@
</properties>
<command>vtysh -c "show ip route tag $5"</command>
</tagNode>
- <node name="vrf">
- <properties>
- <help>Show IP routes in VRF</help>
- </properties>
- </node>
<tagNode name="vrf">
<properties>
<help>Show IP routes in VRF</help>
<completionHelp>
- <list>&lt;vrf&gt;</list>
+ <list>all</list>
<path>vrf name</path>
</completionHelp>
</properties>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 92c1cf016..4c0a7913b 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -6,7 +6,7 @@
<properties>
<help>Show contents of current master log file</help>
</properties>
- <command>/bin/journalctl</command>
+ <command>journalctl --no-hostname --boot</command>
<children>
<leafNode name="all">
<properties>
@@ -18,7 +18,7 @@
<properties>
<help>Show listing of authorization attempts</help>
</properties>
- <command>/bin/journalctl --quiet SYSLOG_FACILITY=10 SYSLOG_FACILITY=4</command>
+ <command>journalctl --no-hostname --boot --quiet SYSLOG_FACILITY=10 SYSLOG_FACILITY=4</command>
</leafNode>
<leafNode name="cluster">
<properties>
@@ -30,14 +30,68 @@
<properties>
<help>Show log for Conntrack-sync</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr ) | grep -e conntrackd</command>
+ <command>journalctl --no-hostname --boot --unit conntrackd.service</command>
</leafNode>
- <leafNode name="dhcp">
+ <node name="dhcp">
<properties>
<help>Show log for Dynamic Host Control Protocol (DHCP)</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep dhcpd</command>
- </leafNode>
+ <children>
+ <node name="server">
+ <properties>
+ <help>Show log for DHCP server</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit isc-dhcp-server.service</command>
+ </node>
+ <node name="client">
+ <properties>
+ <help>Show DHCP client logs</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit "dhclient@*.service"</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show DHCP client log on specific interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit "dhclient@$6.service"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="dhcpv6">
+ <properties>
+ <help>Show log for Dynamic Host Control Protocol IPv6 (DHCPv6)</help>
+ </properties>
+ <children>
+ <node name="server">
+ <properties>
+ <help>Show log for DHCPv6 server</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit isc-dhcp-server6.service</command>
+ </node>
+ <node name="client">
+ <properties>
+ <help>Show DHCPv6 client logs</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit "dhcp6c@*.service"</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show DHCPv6 client log on specific interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit "dhcp6c@$6.service"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
<node name="firewall">
<properties>
<help>Show log for Firewall</help>
@@ -89,7 +143,7 @@
<properties>
<help>Show log for HTTPs</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e nginx</command>
+ <command>journalctl --no-hostname --boot --unit nginx.service</command>
</leafNode>
<tagNode name="image">
<properties>
@@ -119,7 +173,7 @@
<list>&lt;NUMBER&gt;</list>
</completionHelp>
</properties>
- <command>tail -n "$6" /lib/live/mount/persistence/boot/$4/rw/var/log/messages | ${VYATTA_PAGER:-cat}</command>
+ <command>tail -n "$6" /lib/live/mount/persistence/boot/$4/rw/var/log/messages | ${VYATTA_PAGER:-cat}</command>
</tagNode>
</children>
</tagNode>
@@ -133,7 +187,7 @@
<properties>
<help>Show log for LLDP</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e lldpd</command>
+ <command>journalctl --no-hostname --boot --unit lldpd.service</command>
</leafNode>
<leafNode name="nat">
<properties>
@@ -141,17 +195,28 @@
</properties>
<command>egrep -i "kernel:.*\[NAT-[A-Z]{3,}-[0-9]+(-MASQ)?\]" $(find /var/log -maxdepth 1 -type f -name messages\* | sort -t. -k2nr)</command>
</leafNode>
- <leafNode name="openvpn">
+ <node name="openvpn">
<properties>
<help>Show log for OpenVPN</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e openvpn</command>
- </leafNode>
+ <command>journalctl --no-hostname --boot --unit openvpn@*.service</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show OpenVPN log on specific interface</help>
+ <completionHelp>
+ <path>interfaces openvpn</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit openvpn@$5.service</command>
+ </tagNode>
+ </children>
+ </node>
<leafNode name="snmp">
<properties>
<help>Show log for Simple Network Monitoring Protocol (SNMP)</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e snmpd</command>
+ <command>journalctl --no-hostname --boot --unit snmpd.service</command>
</leafNode>
<tagNode name="tail">
<properties>
@@ -195,13 +260,13 @@
<properties>
<help>Show log for PPTP</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e accel-pptp -e ppp</command>
+ <command>journalctl --no-hostname --boot --unit accel-ppp@pptp.service</command>
</leafNode>
<leafNode name="sstp">
<properties>
<help>Show log for SSTP</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e accel-sstp -e ppp</command>
+ <command>journalctl --no-hostname --boot --unit accel-ppp@sstp.service</command>
</leafNode>
</children>
</node>
@@ -209,13 +274,13 @@
<properties>
<help>Show log for Virtual Router Redundancy Protocol (VRRP)</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e Keepalived_vrrp</command>
+ <command>journalctl --no-hostname --boot --unit keepalived.service</command>
</leafNode>
<leafNode name="webproxy">
<properties>
<help>Show log for Webproxy</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e "squid"</command>
+ <command>journalctl --no-hostname --boot --unit squid.service</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in
index 5e9bf719e..18a28868d 100644
--- a/op-mode-definitions/show-system.xml.in
+++ b/op-mode-definitions/show-system.xml.in
@@ -55,12 +55,6 @@
</properties>
<command>${vyos_op_scripts_dir}/show_cpu.py</command>
</leafNode>
- <leafNode name= "integrity">
- <properties>
- <help>Checks overall system integrity</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/show_system_integrity.py</command>
- </leafNode>
<leafNode name="kernel-messages">
<properties>
<help>Show messages in kernel ring buffer</help>
diff --git a/op-mode-definitions/terminal.xml.in b/op-mode-definitions/terminal.xml.in
index 9c4e629cb..2a76de146 100644
--- a/op-mode-definitions/terminal.xml.in
+++ b/op-mode-definitions/terminal.xml.in
@@ -40,7 +40,6 @@
</properties>
<command>builtin $3</command>
</tagNode>
-
<node name="console">
<properties>
<help>Control console behaviors</help>
@@ -54,13 +53,11 @@
</leafNode>
</children>
</node>
-
<node name="terminal">
<properties>
<help>Control terminal behaviors</help>
</properties>
<children>
-
<node name="key">
<properties>
<help>Set key behaviors</help>
@@ -77,7 +74,6 @@
</tagNode>
</children>
</node>
-
<node name="pager">
<properties>
<help>Set terminal pager to default (less)</help>
@@ -93,7 +89,6 @@
</properties>
<command>VYATTA_PAGER=$4</command>
</tagNode>
-
<tagNode name="length">
<properties>
<help>Set terminal to given number of rows (0 disables paging)</help>
@@ -103,7 +98,6 @@
</properties>
<command>if [ "$4" -eq 0 ]; then VYATTA_PAGER=cat; else VYATTA_PAGER=${_vyatta_default_pager}; stty rows $4; fi</command>
</tagNode>
-
<tagNode name="width">
<properties>
<help>Set terminal to given number of columns</help>
@@ -117,6 +111,4 @@
</node>
</children>
</node>
-
-
</interfaceDefinition>
diff --git a/op-mode-definitions/wireguard.xml.in b/op-mode-definitions/wireguard.xml.in
index 22b08002d..e2bc8a590 100644
--- a/op-mode-definitions/wireguard.xml.in
+++ b/op-mode-definitions/wireguard.xml.in
@@ -116,59 +116,6 @@
</node>
</children>
</node>
- <node name="interfaces">
- <children>
- <tagNode name="wireguard">
- <properties>
- <help>show wireguard interface information</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --type wireguard</script>
- </completionHelp>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/wireguard.py --showinterface "$4"</command>
- <children>
- <leafNode name="allowed-ips">
- <properties>
- <help>show all allowed-ips for the specified interface</help>
- </properties>
- <command>sudo wg show "$4" allowed-ips</command>
- </leafNode>
- <leafNode name="endpoints">
- <properties>
- <help>show all endpoints for the specified interface</help>
- </properties>
- <command>sudo wg show "$4" endpoints</command>
- </leafNode>
- <leafNode name="peers">
- <properties>
- <help>show all peer IDs for the specified interface</help>
- </properties>
- <command>sudo wg show "$4" peers</command>
- </leafNode>
- <leafNode name="summary">
- <properties>
- <help>Shows current configuration and device information</help>
- </properties>
- <command>sudo wg show "$4"</command>
- </leafNode>
- </children>
- </tagNode>
- <node name="wireguard">
- <properties>
- <help>Show wireguard interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show-brief</command>
- <children>
- <leafNode name="detail">
- <properties>
- <help>Show detailed wireguard interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show</command>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
</children>
</node>
<node name="delete">
diff --git a/op-mode-definitions/wireless.xml.in b/op-mode-definitions/wireless.xml.in
index a3a9d1f55..5d9db1544 100644
--- a/op-mode-definitions/wireless.xml.in
+++ b/op-mode-definitions/wireless.xml.in
@@ -37,83 +37,4 @@
</node>
</children>
</node>
- <node name="show">
- <children>
- <node name="interfaces">
- <children>
- <node name="wireless">
- <properties>
- <help>Show wireless interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show-brief</command>
- <children>
- <leafNode name="detail">
- <properties>
- <help>Show detailed wireless interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show</command>
- </leafNode>
- <leafNode name="info">
- <properties>
- <help>Show wireless interface configuration</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_wireless.py --brief</command>
- </leafNode>
- </children>
- </node>
- <tagNode name="wireless">
- <properties>
- <help>Show specified wireless interface information</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --type wireless</script>
- </completionHelp>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4"</command>
- <children>
- <leafNode name="brief">
- <properties>
- <help>Show summary of the specified wireless interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" --action=show-brief</command>
- </leafNode>
- <node name="scan">
- <properties>
- <help>Show summary of the specified wireless interface information</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/show_wireless.py --scan "$4"</command>
- <children>
- <leafNode name="detail">
- <properties>
- <help>Show detailed scan results</help>
- </properties>
- <command>sudo /sbin/iw dev "$4" scan ap-force</command>
- </leafNode>
- </children>
- </node>
- <leafNode name="stations">
- <properties>
- <help>Show specified wireless interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_wireless.py --stations "$4"</command>
- </leafNode>
- <tagNode name="vif">
- <properties>
- <help>Show specified virtual network interface (vif) information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6"</command>
- <children>
- <leafNode name="brief">
- <properties>
- <help>Show summary of specified virtual network interface (vif) information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6" --action=show-brief</command>
- </leafNode>
- </children>
- </tagNode>
- </children>
- </tagNode>
- </children>
- </node>
- </children>
- </node>
</interfaceDefinition>
diff --git a/python/vyos/airbag.py b/python/vyos/airbag.py
index a20f44207..3c7a144b7 100644
--- a/python/vyos/airbag.py
+++ b/python/vyos/airbag.py
@@ -125,14 +125,14 @@ def _intercepting_exceptions(_singleton=[False]):
# if the key before the value has not time, syslog takes that as the source of the message
FAULT = """\
-Report Time: {date}
-Image Version: VyOS {version}
-Release Train: {release_train}
+Report time: {date}
+Image version: VyOS {version}
+Release train: {release_train}
Built by: {built_by}
Built on: {built_on}
Build UUID: {build_uuid}
-Build Commit ID: {build_git}
+Build commit ID: {build_git}
Architecture: {system_arch}
Boot via: {boot_via}
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index dba992d56..8e5781b81 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -108,16 +108,20 @@ def leaf_node_changed(conf, path):
"""
Check if a leaf node was altered. If it has been altered - values has been
changed, or it was added/removed, we will return a list containing the old
- value(s). If nothing has been changed, None is returned
+ value(s). If nothing has been changed, None is returned.
+
+ NOTE: path must use the real CLI node name (e.g. with a hyphen!)
"""
from vyos.configdiff import get_config_diff
D = get_config_diff(conf, key_mangling=('-', '_'))
D.set_level(conf.get_level())
(new, old) = D.get_value_diff(path)
if new != old:
+ if old is None:
+ return []
if isinstance(old, str):
return [old]
- elif isinstance(old, list):
+ if isinstance(old, list):
if isinstance(new, str):
new = [new]
elif isinstance(new, type(None)):
@@ -151,18 +155,15 @@ def get_removed_vlans(conf, dict):
D.set_level(conf.get_level())
# get_child_nodes() will return dict_keys(), mangle this into a list with PEP448
keys = D.get_child_nodes_diff(['vif'], expand_nodes=Diff.DELETE)['delete'].keys()
- if keys:
- dict.update({'vif_remove': [*keys]})
+ if keys: dict['vif_remove'] = [*keys]
# get_child_nodes() will return dict_keys(), mangle this into a list with PEP448
keys = D.get_child_nodes_diff(['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys()
- if keys:
- dict.update({'vif_s_remove': [*keys]})
+ if keys: dict['vif_s_remove'] = [*keys]
for vif in dict.get('vif_s', {}).keys():
keys = D.get_child_nodes_diff(['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys()
- if keys:
- dict.update({'vif_s': { vif : {'vif_c_remove': [*keys]}}})
+ if keys: dict['vif_s'][vif]['vif_c_remove'] = [*keys]
return dict
@@ -343,8 +344,8 @@ def get_interface_dict(config, base, ifname=''):
# setup config level which is extracted in get_removed_vlans()
config.set_level(base + [ifname])
- dict = config.get_config_dict([], key_mangling=('-', '_'),
- get_first_key=True)
+ dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
# Check if interface has been removed. We must use exists() as
# get_config_dict() will always return {} - even when an empty interface
@@ -371,6 +372,9 @@ def get_interface_dict(config, base, ifname=''):
# XXX: T2665: blend in proper DHCPv6-PD default values
dict = T2665_set_dhcpv6pd_defaults(dict)
+ address = leaf_node_changed(config, ['address'])
+ if address: dict.update({'address_old' : address})
+
# Check if we are a member of a bridge device
bridge = is_member(config, ifname, 'bridge')
if bridge: dict.update({'is_bridge_member' : bridge})
@@ -515,6 +519,11 @@ def get_accel_dict(config, base, chap_secrets):
if dict_search('authentication.local_users.username', default_values):
del default_values['authentication']['local_users']['username']
+ # T2665: defaults include IPv6 client-pool mask per TAG node which need to be
+ # added to individual local users instead - so we can simply delete them
+ if dict_search('client_ipv6_pool.prefix.mask', default_values):
+ del default_values['client_ipv6_pool']['prefix']['mask']
+
dict = dict_merge(default_values, dict)
# set CPUs cores to process requests
@@ -558,4 +567,13 @@ def get_accel_dict(config, base, chap_secrets):
dict['authentication']['local_users']['username'][username] = dict_merge(
default_values, dict['authentication']['local_users']['username'][username])
+ # Add individual IPv6 client-pool default mask if required
+ if dict_search('client_ipv6_pool.prefix', dict):
+ # T2665
+ default_values = defaults(base + ['client-ipv6-pool', 'prefix'])
+
+ for prefix in dict_search('client_ipv6_pool.prefix', dict):
+ dict['client_ipv6_pool']['prefix'][prefix] = dict_merge(
+ default_values, dict['client_ipv6_pool']['prefix'][prefix])
+
return dict
diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py
index 50222e385..b0981d25e 100644
--- a/python/vyos/configsource.py
+++ b/python/vyos/configsource.py
@@ -161,7 +161,7 @@ class ConfigSourceSession(ConfigSource):
if p.returncode != 0:
raise VyOSError()
else:
- return out.decode('ascii')
+ return out.decode('ascii', 'ignore')
def set_level(self, path):
"""
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 0b6e6fc13..3aece499e 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -67,22 +67,22 @@ def verify_mtu_ipv6(config):
min_mtu = 1280
if int(config['mtu']) < min_mtu:
interface = config['ifname']
- error_msg = f'IPv6 address will be configured on interface "{interface}" ' \
- f'thus the minimum MTU requirement is {min_mtu}!'
+ error_msg = f'IPv6 address will be configured on interface "{interface}",\n' \
+ f'the required minimum MTU is {min_mtu}!'
- for address in (dict_search('address', config) or []):
- if address in ['dhcpv6'] or is_ipv6(address):
- raise ConfigError(error_msg)
+ if 'address' in config:
+ for address in config['address']:
+ if address in ['dhcpv6'] or is_ipv6(address):
+ 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)
+ tmp = dict_search('ipv6.address.no_default_link_local', config)
+ if tmp == None: raise ConfigError('link-local ' + error_msg)
- if tmp and 'autoconf' in tmp:
- raise ConfigError(error_msg)
+ tmp = dict_search('ipv6.address.autoconf', config)
+ if tmp != None: raise ConfigError(error_msg)
- if tmp and 'eui64' in tmp:
- raise ConfigError(error_msg)
+ tmp = dict_search('ipv6.address.eui64', config)
+ if tmp != None: raise ConfigError(error_msg)
def verify_tunnel(config):
"""
@@ -95,15 +95,12 @@ def verify_tunnel(config):
raise ConfigError('Must configure the tunnel encapsulation for '\
'{ifname}!'.format(**config))
- if 'source_address' not in config and 'dhcp_interface' not in config:
- raise ConfigError('source-address is mandatory for tunnel')
+ if 'source_address' not in config and 'source_interface' not in config:
+ raise ConfigError('source-address or source-interface required for tunnel!')
if 'remote' not in config and config['encapsulation'] != 'gre':
raise ConfigError('remote-ip address is mandatory for tunnel')
- if {'source_address', 'dhcp_interface'} <= set(config):
- raise ConfigError('Can not use both source-address and dhcp-interface')
-
if config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
error_ipv6 = 'Encapsulation mode requires IPv6'
if 'source_address' in config and not is_ipv6(config['source_address']):
@@ -208,8 +205,8 @@ def verify_interface_exists(ifname):
Common helper function used by interface implementations to perform
recurring validation if an interface actually exists.
"""
- from netifaces import interfaces
- if ifname not in interfaces():
+ import os
+ if not os.path.exists(f'/sys/class/net/{ifname}'):
raise ConfigError(f'Interface "{ifname}" does not exist!')
def verify_source_interface(config):
@@ -385,3 +382,29 @@ def verify_diffie_hellman_length(file, min_keysize):
return False
+def verify_common_route_maps(config):
+ """
+ Common helper function used by routing protocol implementations to perform
+ recurring validation if the specified route-map for either zebra to kernel
+ installation exists (this is the top-level route_map key) or when a route
+ is redistributed with a route-map that it exists!
+ """
+ # XXX: This function is called in combination with a previous call to:
+ # tmp = conf.get_config_dict(['policy']) - see protocols_ospf.py as example.
+ # We should NOT call this with the key_mangling option as this would rename
+ # route-map hypens '-' to underscores '_' and one could no longer distinguish
+ # what should have been the "proper" route-map name, as foo-bar and foo_bar
+ # are two entire different route-map instances!
+ for route_map in ['route-map', 'route_map']:
+ if route_map not in config:
+ continue
+ tmp = config[route_map]
+ # Check if the specified route-map exists, if not error out
+ if dict_search(f'policy.route-map.{tmp}', config) == None:
+ raise ConfigError(f'Specified route-map "{tmp}" does not exist!')
+
+ if 'redistribute' in config:
+ for protocol, protocol_config in config['redistribute'].items():
+ if 'route_map' in protocol_config:
+ verify_route_map(protocol_config['route_map'], config)
+
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index 9921e3b5f..dacdbdef2 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -13,6 +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 os
directories = {
"data": "/usr/share/vyos/",
@@ -22,7 +23,10 @@ directories = {
"migrate": "/opt/vyatta/etc/config-migrate/migrate",
"log": "/var/log/vyatta",
"templates": "/usr/share/vyos/templates/",
- "certbot": "/config/auth/letsencrypt"
+ "certbot": "/config/auth/letsencrypt",
+ "api_schema": "/usr/libexec/vyos/services/api/graphql/graphql/schema/",
+ "api_templates": "/usr/libexec/vyos/services/api/graphql/recipes/templates/"
+
}
cfg_group = 'vyattacfg'
@@ -31,7 +35,7 @@ cfg_vintage = 'vyos'
commit_lock = '/opt/vyatta/config/.lock'
-version_file = '/usr/share/vyos/component-versions.json'
+component_version_json = os.path.join(directories['data'], 'component-versions.json')
https_data = {
'listen_addresses' : { '*': ['_'] }
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index bc103959a..e45b0f041 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -13,44 +13,89 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+import os
+import re
+
from vyos.util import popen
class Ethtool:
"""
Class is used to retrive and cache information about an ethernet adapter
"""
-
# dictionary containing driver featurs, it will be populated on demand and
# the content will look like:
# {
- # 'tls-hw-tx-offload': {'fixed': True, 'on': False},
- # 'tx-checksum-fcoe-crc': {'fixed': True, 'on': False},
- # 'tx-checksum-ip-generic': {'fixed': False, 'on': True},
- # 'tx-checksum-ipv4': {'fixed': True, 'on': False},
- # 'tx-checksum-ipv6': {'fixed': True, 'on': False},
- # 'tx-checksum-sctp': {'fixed': True, 'on': False},
- # 'tx-checksumming': {'fixed': False, 'on': True},
- # 'tx-esp-segmentation': {'fixed': True, 'on': False},
+ # 'tls-hw-tx-offload': {'fixed': True, 'enabled': False},
+ # 'tx-checksum-fcoe-crc': {'fixed': True, 'enabled': False},
+ # 'tx-checksum-ip-generic': {'fixed': False, 'enabled': True},
+ # 'tx-checksum-ipv4': {'fixed': True, 'enabled': False},
+ # 'tx-checksum-ipv6': {'fixed': True, 'enabled': False},
+ # 'tx-checksum-sctp': {'fixed': True, 'enabled': False},
+ # 'tx-checksumming': {'fixed': False, 'enabled': True},
+ # 'tx-esp-segmentation': {'fixed': True, 'enabled': False},
# }
- features = { }
- ring_buffers = { }
+ _features = { }
+ # dictionary containing available interface speed and duplex settings
+ # {
+ # '10' : {'full': '', 'half': ''},
+ # '100' : {'full': '', 'half': ''},
+ # '1000': {'full': ''}
+ # }
+ _speed_duplex = { }
+ _ring_buffers = { }
+ _ring_buffers_max = { }
+ _driver_name = None
+ _auto_negotiation = False
+ _flow_control = False
+ _flow_control_enabled = None
def __init__(self, ifname):
+ # Get driver used for interface
+ sysfs_file = f'/sys/class/net/{ifname}/device/driver/module'
+ if os.path.exists(sysfs_file):
+ link = os.readlink(sysfs_file)
+ self._driver_name = os.path.basename(link)
+
+ # Build a dictinary of supported link-speed and dupley settings.
+ out, err = popen(f'ethtool {ifname}')
+ reading = False
+ pattern = re.compile(r'\d+base.*')
+ for line in out.splitlines()[1:]:
+ line = line.lstrip()
+ if 'Supported link modes:' in line:
+ reading = True
+ if 'Supported pause frame use:' in line:
+ reading = False
+ if reading:
+ for block in line.split():
+ if pattern.search(block):
+ speed = block.split('base')[0]
+ duplex = block.split('/')[-1].lower()
+ if speed not in self._speed_duplex:
+ self._speed_duplex.update({ speed : {}})
+ if duplex not in self._speed_duplex[speed]:
+ self._speed_duplex[speed].update({ duplex : ''})
+ if 'Auto-negotiation:' in line:
+ # Split the following string: Auto-negotiation: off
+ # we are only interested in off or on
+ tmp = line.split()[-1]
+ self._auto_negotiation = bool(tmp == 'on')
+
# Now populate features dictionaty
- out, err = popen(f'ethtool -k {ifname}')
+ out, err = popen(f'ethtool --show-features {ifname}')
# skip the first line, it only says: "Features for eth0":
for line in out.splitlines()[1:]:
if ":" in line:
key, value = [s.strip() for s in line.strip().split(":", 1)]
- fixed = "fixed" in value
+ fixed = bool('fixed' in value)
if fixed:
value = value.split()[0].strip()
- self.features[key.strip()] = {
- "on": value == "on",
- "fixed": fixed
+ self._features[key.strip()] = {
+ 'enabled' : bool(value == 'on'),
+ 'fixed' : fixed
}
- out, err = popen(f'ethtool -g {ifname}')
+ out, err = popen(f'ethtool --show-ring {ifname}')
# We are only interested in line 2-5 which contains the device maximum
# ringbuffers
for line in out.splitlines()[2:6]:
@@ -61,45 +106,104 @@ class Ethtool:
# output format from 0 -> n/a. As we are only interested in the
# tx/rx keys we do not care about RX Mini/Jumbo.
if value.isdigit():
- self.ring_buffers[key] = int(value)
+ self._ring_buffers_max[key] = value
+ # Now we wan't to get the current RX/TX ringbuffer values - used for
+ for line in out.splitlines()[7:11]:
+ if ':' in line:
+ key, value = [s.strip() for s in line.strip().split(":", 1)]
+ key = key.lower().replace(' ', '_')
+ # T3645: ethtool version used on Debian Bullseye changed the
+ # output format from 0 -> n/a. As we are only interested in the
+ # tx/rx keys we do not care about RX Mini/Jumbo.
+ if value.isdigit():
+ self._ring_buffers[key] = value
+
+ # Get current flow control settings, but this is not supported by
+ # all NICs (e.g. vmxnet3 does not support is)
+ out, err = popen(f'ethtool --show-pause {ifname}')
+ if len(out.splitlines()) > 1:
+ self._flow_control = True
+ # read current flow control setting, this returns:
+ # ['Autonegotiate:', 'on']
+ self._flow_control_enabled = out.splitlines()[1].split()[-1]
+
+ def get_auto_negotiation(self):
+ return self._auto_negotiation
+
+ def get_driver_name(self):
+ return self._driver_name
+ def _get_generic(self, feature):
+ """
+ Generic method to read self._features and return a tuple for feature
+ enabled and feature is fixed.
- def is_fixed_lro(self):
- # in case of a missing configuration, rather return "fixed". In Ethtool
- # terminology "fixed" means the setting can not be changed by the user.
- return self.features.get('large-receive-offload', True).get('fixed', True)
+ In case of a missing key, return "fixed = True and enabled = False"
+ """
+ fixed = True
+ enabled = False
+ if feature in self._features:
+ if 'enabled' in self._features[feature]:
+ enabled = self._features[feature]['enabled']
+ if 'fixed' in self._features[feature]:
+ fixed = self._features[feature]['fixed']
+ return enabled, fixed
- def is_fixed_gro(self):
- # in case of a missing configuration, rather return "fixed". In Ethtool
- # terminology "fixed" means the setting can not be changed by the user.
- return self.features.get('generic-receive-offload', True).get('fixed', True)
+ def get_generic_receive_offload(self):
+ return self._get_generic('generic-receive-offload')
- def is_fixed_gso(self):
- # in case of a missing configuration, rather return "fixed". In Ethtool
- # terminology "fixed" means the setting can not be changed by the user.
- return self.features.get('generic-segmentation-offload', True).get('fixed', True)
+ def get_generic_segmentation_offload(self):
+ return self._get_generic('generic-segmentation-offload')
- def is_fixed_sg(self):
- # in case of a missing configuration, rather return "fixed". In Ethtool
- # terminology "fixed" means the setting can not be changed by the user.
- return self.features.get('scatter-gather', True).get('fixed', True)
+ def get_large_receive_offload(self):
+ return self._get_generic('large-receive-offload')
- def is_fixed_tso(self):
- # in case of a missing configuration, rather return "fixed". In Ethtool
- # terminology "fixed" means the setting can not be changed by the user.
- return self.features.get('tcp-segmentation-offload', True).get('fixed', True)
+ def get_scatter_gather(self):
+ return self._get_generic('scatter-gather')
- def is_fixed_ufo(self):
- # in case of a missing configuration, rather return "fixed". In Ethtool
- # terminology "fixed" means the setting can not be changed by the user.
- return self.features.get('udp-fragmentation-offload', True).get('fixed', True)
+ def get_tcp_segmentation_offload(self):
+ return self._get_generic('tcp-segmentation-offload')
- def get_rx_buffer(self):
- # Configuration of RX ring-buffers is not supported on every device,
+ def get_ring_buffer_max(self, rx_tx):
+ # Configuration of RX/TX ring-buffers is not supported on every device,
# thus when it's impossible return None
- return self.ring_buffers.get('rx', None)
+ if rx_tx not in ['rx', 'tx']:
+ ValueError('Ring-buffer type must be either "rx" or "tx"')
+ return self._ring_buffers_max.get(rx_tx, None)
- def get_tx_buffer(self):
- # Configuration of TX ring-buffers is not supported on every device,
+ def get_ring_buffer(self, rx_tx):
+ # Configuration of RX/TX ring-buffers is not supported on every device,
# thus when it's impossible return None
- return self.ring_buffers.get('tx', None)
+ if rx_tx not in ['rx', 'tx']:
+ ValueError('Ring-buffer type must be either "rx" or "tx"')
+ return str(self._ring_buffers.get(rx_tx, None))
+
+ def check_speed_duplex(self, speed, duplex):
+ """ Check if the passed speed and duplex combination is supported by
+ the underlaying network adapter. """
+ if isinstance(speed, int):
+ speed = str(speed)
+ if speed != 'auto' and not speed.isdigit():
+ raise ValueError(f'Value "{speed}" for speed is invalid!')
+ if duplex not in ['auto', 'full', 'half']:
+ raise ValueError(f'Value "{duplex}" for duplex is invalid!')
+
+ if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']:
+ return False
+
+ if speed in self._speed_duplex:
+ if duplex in self._speed_duplex[speed]:
+ return True
+ return False
+
+ def check_flow_control(self):
+ """ Check if the NIC supports flow-control """
+ if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']:
+ return False
+ return self._flow_control
+
+ def get_flow_control(self):
+ if self._flow_control_enabled == None:
+ raise ValueError('Interface does not support changing '\
+ 'flow-control settings!')
+ return self._flow_control_enabled
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index 3bab64301..df6849472 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -68,15 +68,27 @@ Apply the new configuration:
import tempfile
import re
from vyos import util
+from vyos.util import chown
+from vyos.util import cmd
import logging
+from logging.handlers import SysLogHandler
+import os
LOG = logging.getLogger(__name__)
+DEBUG = os.path.exists('/tmp/vyos.frr.debug')
+if DEBUG:
+ LOG.setLevel(logging.DEBUG)
+ ch = SysLogHandler(address='/dev/log')
+ ch2 = logging.StreamHandler()
+ LOG.addHandler(ch)
+ LOG.addHandler(ch2)
_frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd',
'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd']
path_vtysh = '/usr/bin/vtysh'
path_frr_reload = '/usr/lib/frr/frr-reload.py'
+path_config = '/run/frr'
class FrrError(Exception):
@@ -175,21 +187,42 @@ def reload_configuration(config, daemon=None):
f.write(config)
f.flush()
+ LOG.debug(f'reload_configuration: Reloading config using temporary file: {f.name}')
cmd = f'{path_frr_reload} --reload'
if daemon:
cmd += f' --daemon {daemon}'
+
+ if DEBUG:
+ cmd += f' --debug --stdout'
+
cmd += f' {f.name}'
+ LOG.debug(f'reload_configuration: Executing command against frr-reload: "{cmd}"')
output, code = util.popen(cmd, stderr=util.STDOUT)
f.close()
+ for i, e in enumerate(output.split('\n')):
+ LOG.debug(f'frr-reload output: {i:3} {e}')
if code == 1:
- raise CommitError(f'Configuration FRR failed while commiting code: {repr(output)}')
+ raise CommitError('FRR configuration failed while running commit. Please ' \
+ 'enable debugging to examine logs.\n\n\n' \
+ 'To enable debugging run: "touch /tmp/vyos.frr.debug" ' \
+ 'and "sudo systemctl stop vyos-configd"')
elif code:
raise OSError(code, output)
return output
+def save_configuration():
+ """Save FRR configuration to /run/frr/config/frr.conf
+ It save configuration on each commit. T3217
+ """
+
+ cmd(f'{path_vtysh} -n -w')
+
+ return
+
+
def execute(command):
""" Run commands inside vtysh
command: str containing commands to execute inside a vtysh session
@@ -382,6 +415,11 @@ class FRRConfig:
raise ValueError(
'The config element needs to be a string or list type object')
+ if config:
+ LOG.debug(f'__init__: frr library initiated with initial config')
+ for i, e in enumerate(self.config):
+ LOG.debug(f'__init__: initial {i:3} {e}')
+
def load_configuration(self, daemon=None):
'''Load the running configuration from FRR into the config object
daemon: str with name of the FRR Daemon to load configuration from or
@@ -390,9 +428,16 @@ class FRRConfig:
Using this overwrites the current loaded config objects and replaces the original loaded config
'''
self.imported_config = get_configuration(daemon=daemon)
- LOG.debug(f'load_configuration: Configuration loaded from FRR: {self.imported_config}')
+ if daemon:
+ LOG.debug(f'load_configuration: Configuration loaded from FRR daemon {daemon}')
+ else:
+ LOG.debug(f'load_configuration: Configuration loaded from FRR integrated config')
+
self.original_config = self.imported_config.split('\n')
self.config = self.original_config.copy()
+
+ for i, e in enumerate(self.imported_config.split('\n')):
+ LOG.debug(f'load_configuration: loaded {i:3} {e}')
return
def test_configuration(self):
@@ -408,6 +453,8 @@ class FRRConfig:
None to use the consolidated config
'''
LOG.debug('commit_configuration: Commiting configuration')
+ for i, e in enumerate(self.config):
+ LOG.debug(f'commit_configuration: new_config {i:3} {e}')
reload_configuration('\n'.join(self.config), daemon=daemon)
def modify_section(self, start_pattern, replacement=[], stop_pattern=r'\S+', remove_stop_mark=False, count=0):
@@ -459,7 +506,8 @@ class FRRConfig:
start = _find_first_element(self.config, before_pattern)
if start < 0:
return False
-
+ for i, e in enumerate(addition, start=start):
+ LOG.debug(f'add_before: add {i:3} {e}')
self.config[start:start] = addition
return True
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 65a4506c5..27073b266 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2021 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
@@ -22,6 +22,7 @@ from vyos.validate import assert_positive
from vyos.util import cmd
from vyos.util import dict_search
from vyos.configdict import get_vlan_ids
+from vyos.configdict import list_diff
@Interface.register
class BridgeIf(Interface):
@@ -33,7 +34,6 @@ class BridgeIf(Interface):
The Linux bridge code implements a subset of the ANSI/IEEE 802.1d standard.
"""
-
iftype = 'bridge'
definition = {
**Interface.definition,
@@ -267,21 +267,37 @@ class BridgeIf(Interface):
for member in (tmp or []):
if member in interfaces():
self.del_port(member)
- vlan_filter = 0
- vlan_del = set()
- vlan_add = set()
+ # enable/disable Vlan Filter
+ vlan_filter = '1' if 'enable_vlan' in config else '0'
+ self.set_vlan_filter(vlan_filter)
ifname = config['ifname']
+ if int(vlan_filter):
+ add_vlan = []
+ cur_vlan_ids = get_vlan_ids(ifname)
+
+ tmp = dict_search('vif', config)
+ if tmp:
+ for vif, vif_config in tmp.items():
+ add_vlan.append(vif)
+
+ # Remove redundant VLANs from the system
+ for vlan in list_diff(cur_vlan_ids, add_vlan):
+ cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
+ self._cmd(cmd)
+
+ for vlan in add_vlan:
+ cmd = f'bridge vlan add dev {ifname} vid {vlan} self'
+ self._cmd(cmd)
+
+ # VLAN of bridge parent interface is always 1
+ # VLAN 1 is the default VLAN for all unlabeled packets
+ cmd = f'bridge vlan add dev {ifname} vid 1 pvid untagged self'
+ self._cmd(cmd)
+
tmp = dict_search('member.interface', config)
if tmp:
- if self.get_vlan_filter():
- bridge_vlan_ids = get_vlan_ids(ifname)
- # Delete VLAN ID for the bridge
- if 1 in bridge_vlan_ids:
- bridge_vlan_ids.remove(1)
- for vlan in bridge_vlan_ids:
- vlan_del.add(str(vlan))
for interface, interface_config in tmp.items():
# if interface does yet not exist bail out early and
@@ -296,9 +312,15 @@ class BridgeIf(Interface):
# not have any addresses configured by CLI so just flush any
# remaining ones
lower.flush_addrs()
+
# enslave interface port to bridge
self.add_port(interface)
+ # always set private-vlan/port isolation
+ tmp = dict_search('isolated', interface_config)
+ value = 'on' if (tmp != None) else 'off'
+ lower.set_port_isolation(value)
+
# set bridge port path cost
if 'cost' in interface_config:
value = interface_config.get('cost')
@@ -309,62 +331,39 @@ class BridgeIf(Interface):
value = interface_config.get('priority')
lower.set_path_priority(value)
- tmp = dict_search('native_vlan_removed', interface_config)
-
- for vlan_id in (tmp or []):
- cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
- self._cmd(cmd)
- cmd = f'bridge vlan add dev {interface} vid 1 pvid untagged master'
- self._cmd(cmd)
- vlan_del.add(vlan_id)
- vlan_add.add(1)
-
- 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)
- vlan_del.add(vlan_id)
-
- 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']
- if int(vlan_id) != 1:
- if 1 in vlan_add:
- vlan_add.remove(1)
- vlan_del.add(1)
- cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master'
- self._cmd(cmd)
- vlan_add.add(vlan_id)
- if vlan_id in vlan_del:
- vlan_del.remove(vlan_id)
-
- if 'allowed_vlan' in interface_config:
- vlan_filter = 1
- if 'native_vlan' not in interface_config:
- cmd = f'bridge vlan del dev {interface} vid 1'
+ if int(vlan_filter):
+ add_vlan = []
+ native_vlan_id = None
+ allowed_vlan_ids= []
+ cur_vlan_ids = get_vlan_ids(interface)
+
+ if 'native_vlan' in interface_config:
+ vlan_id = interface_config['native_vlan']
+ add_vlan.append(vlan_id)
+ native_vlan_id = vlan_id
+
+ if 'allowed_vlan' in interface_config:
+ for vlan in interface_config['allowed_vlan']:
+ vlan_range = vlan.split('-')
+ if len(vlan_range) == 2:
+ for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1):
+ add_vlan.append(str(vlan_add))
+ allowed_vlan_ids.append(str(vlan_add))
+ else:
+ add_vlan.append(vlan)
+ allowed_vlan_ids.append(vlan)
+
+ # Remove redundant VLANs from the system
+ for vlan in list_diff(cur_vlan_ids, add_vlan):
+ cmd = f'bridge vlan del dev {interface} vid {vlan} master'
self._cmd(cmd)
- vlan_del.add(1)
- for vlan in interface_config['allowed_vlan']:
+
+ for vlan in allowed_vlan_ids:
cmd = f'bridge vlan add dev {interface} vid {vlan} master'
self._cmd(cmd)
- vlan_add.add(vlan)
- if vlan in vlan_del:
- vlan_del.remove(vlan)
-
- for vlan in vlan_del:
- cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
- self._cmd(cmd)
-
- for vlan in vlan_add:
- cmd = f'bridge vlan add dev {ifname} vid {vlan} self'
- self._cmd(cmd)
-
- # enable/disable Vlan Filter
- self.set_vlan_filter(vlan_filter)
-
+ # Setting native VLAN to system
+ if native_vlan_id:
+ cmd = f'bridge vlan add dev {interface} vid {native_vlan_id} pvid untagged master'
+ self._cmd(cmd)
- # call base class first
super().update(config)
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index 43136f361..6815074f8 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -18,11 +18,12 @@ import os
from inspect import signature
from inspect import _empty
-from vyos import debug
+from vyos.ifconfig.section import Section
from vyos.util import popen
from vyos.util import cmd
-from vyos.ifconfig.section import Section
-
+from vyos.util import read_file
+from vyos.util import write_file
+from vyos import debug
class Control(Section):
_command_get = {}
@@ -116,20 +117,18 @@ class Control(Section):
Provide a single primitive w/ error checking for reading from sysfs.
"""
value = None
- with open(filename, 'r') as f:
- value = f.read().rstrip('\n')
-
- self._debug_msg("read '{}' < '{}'".format(value, filename))
+ if os.path.exists(filename):
+ value = read_file(filename)
+ self._debug_msg("read '{}' < '{}'".format(value, filename))
return value
def _write_sysfs(self, filename, value):
"""
Provide a single primitive w/ error checking for writing to sysfs.
"""
- self._debug_msg("write '{}' > '{}'".format(value, filename))
if os.path.isfile(filename):
- with open(filename, 'w') as f:
- f.write(str(value))
+ write_file(filename, str(value))
+ self._debug_msg("write '{}' > '{}'".format(value, filename))
return True
return False
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index df6b96fbf..4ae350634 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -16,9 +16,11 @@
import os
import re
+from vyos.ethtool import Ethtool
from vyos.ifconfig.interface import Interface
from vyos.util import run
from vyos.util import dict_search
+from vyos.util import read_file
from vyos.validate import assert_list
@Interface.register
@@ -42,39 +44,29 @@ class EthernetIf(Interface):
@staticmethod
def feature(ifname, option, value):
- run(f'ethtool -K {ifname} {option} {value}','ifconfig')
+ run(f'ethtool --features {ifname} {option} {value}')
return False
_command_set = {**Interface._command_set, **{
'gro': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'gro', v),
- # 'shellcmd': 'ethtool -K {ifname} gro {value}',
},
'gso': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'gso', v),
- # 'shellcmd': 'ethtool -K {ifname} gso {value}',
},
'lro': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'lro', v),
- # 'shellcmd': 'ethtool -K {ifname} lro {value}',
},
'sg': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'sg', v),
- # 'shellcmd': 'ethtool -K {ifname} sg {value}',
},
'tso': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'tso', v),
- # 'shellcmd': 'ethtool -K {ifname} tso {value}',
- },
- 'ufo': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'ufo', v),
- # 'shellcmd': 'ethtool -K {ifname} ufo {value}',
},
}}
@@ -85,24 +77,26 @@ class EthernetIf(Interface):
},
}}
- def get_driver_name(self):
- """
- Return the driver name used by NIC. Some NICs don't support all
- features e.g. changing link-speed, duplex
+ def __init__(self, ifname, **kargs):
+ super().__init__(ifname, **kargs)
+ self.ethtool = Ethtool(ifname)
+ def remove(self):
+ """
+ Remove interface from config. Removing the interface deconfigures all
+ assigned IP addresses.
Example:
- >>> from vyos.ifconfig import EthernetIf
+ >>> from vyos.ifconfig import WWANIf
>>> i = EthernetIf('eth0')
- >>> i.get_driver_name()
- 'vmxnet3'
+ >>> i.remove()
"""
- ifname = self.config['ifname']
- sysfs_file = f'/sys/class/net/{ifname}/device/driver/module'
- if os.path.exists(sysfs_file):
- link = os.readlink(sysfs_file)
- return os.path.basename(link)
- else:
- return None
+
+ if self.exists(self.ifname):
+ # interface is placed in A/D state when removed from config! It
+ # will remain visible for the operating system.
+ self.set_admin_state('down')
+
+ super().remove()
def set_flow_control(self, enable):
"""
@@ -120,44 +114,20 @@ class EthernetIf(Interface):
if enable not in ['on', 'off']:
raise ValueError("Value out of range")
- driver_name = self.get_driver_name()
- if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']:
- self._debug_msg(f'{driver_name} driver does not support changing '\
- 'flow control settings!')
- return
-
- # Get current flow control settings:
- cmd = f'ethtool --show-pause {ifname}'
- output, code = self._popen(cmd)
- if code == 76:
- # the interface does not support it
- return ''
- if code:
- # never fail here as it prevent vyos to boot
- print(f'unexpected return code {code} from {cmd}')
- return ''
-
- # The above command returns - with tabs:
- #
- # Pause parameters for eth0:
- # Autonegotiate: on
- # RX: off
- # TX: off
- if re.search("Autonegotiate:\ton", output):
- if enable == "on":
- # flowcontrol is already enabled - no need to re-enable it again
- # this will prevent the interface from flapping as applying the
- # flow-control settings will take the interface down and bring
- # it back up every time.
- return ''
-
- # Assemble command executed on system. Unfortunately there is no way
- # to change this setting via sysfs
- cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}'
- output, code = self._popen(cmd)
- if code:
- print(f'could not set flowcontrol for {ifname}')
- return output
+ if not self.ethtool.check_flow_control():
+ self._debug_msg(f'NIC driver does not support changing flow control settings!')
+ return False
+
+ current = self.ethtool.get_flow_control()
+ if current != enable:
+ # Assemble command executed on system. Unfortunately there is no way
+ # to change this setting via sysfs
+ cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}'
+ output, code = self._popen(cmd)
+ if code:
+ print(f'Could not set flowcontrol for {ifname}')
+ return output
+ return None
def set_speed_duplex(self, speed, duplex):
"""
@@ -179,40 +149,28 @@ class EthernetIf(Interface):
if duplex not in ['auto', 'full', 'half']:
raise ValueError("Value out of range (duplex)")
- driver_name = self.get_driver_name()
- if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']:
- self._debug_msg(f'{driver_name} driver does not support changing '\
- 'speed/duplex settings!')
+ if not self.ethtool.check_speed_duplex(speed, duplex):
+ self._debug_msg(f'NIC driver does not support changing speed/duplex settings!')
return
# Get current speed and duplex settings:
ifname = self.config['ifname']
- cmd = f'ethtool {ifname}'
- tmp = self._cmd(cmd)
-
- if re.search("\tAuto-negotiation: on", tmp):
+ if self.ethtool.get_auto_negotiation():
if speed == 'auto' and duplex == 'auto':
# bail out early as nothing is to change
return
else:
- # read in current speed and duplex settings
- cur_speed = 0
- cur_duplex = ''
- for line in tmp.splitlines():
- if line.lstrip().startswith("Speed:"):
- non_decimal = re.compile(r'[^\d.]+')
- cur_speed = non_decimal.sub('', line)
- continue
-
- if line.lstrip().startswith("Duplex:"):
- cur_duplex = line.split()[-1].lower()
- break
-
+ # XXX: read in current speed and duplex settings
+ # There are some "nice" NICs like AX88179 which do not support
+ # reading the speed thus we simply fallback to the supplied speed
+ # to not cause any change here and raise an exception.
+ cur_speed = read_file(f'/sys/class/net/{ifname}/speed', speed)
+ cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex', duplex)
if (cur_speed == speed) and (cur_duplex == duplex):
# bail out early as nothing is to change
return
- cmd = f'ethtool -s {ifname}'
+ cmd = f'ethtool --change {ifname}'
if speed == 'auto' or duplex == 'auto':
cmd += ' autoneg on'
else:
@@ -229,8 +187,15 @@ class EthernetIf(Interface):
>>> i.set_gro(True)
"""
if not isinstance(state, bool):
- raise ValueError("Value out of range")
- return self.set_interface('gro', 'on' if state else 'off')
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_generic_receive_offload()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('gro', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing generic-receive-offload settings!')
+ return False
def set_gso(self, state):
"""
@@ -241,8 +206,15 @@ class EthernetIf(Interface):
>>> i.set_gso(True)
"""
if not isinstance(state, bool):
- raise ValueError("Value out of range")
- return self.set_interface('gso', 'on' if state else 'off')
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_generic_segmentation_offload()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('gso', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing generic-segmentation-offload settings!')
+ return False
def set_lro(self, state):
"""
@@ -253,12 +225,19 @@ class EthernetIf(Interface):
>>> i.set_lro(True)
"""
if not isinstance(state, bool):
- raise ValueError("Value out of range")
- return self.set_interface('lro', 'on' if state else 'off')
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_large_receive_offload()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('gro', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing large-receive-offload settings!')
+ return False
def set_rps(self, state):
if not isinstance(state, bool):
- raise ValueError("Value out of range")
+ raise ValueError('Value out of range')
rps_cpus = '0'
if state:
@@ -283,8 +262,15 @@ class EthernetIf(Interface):
>>> i.set_sg(True)
"""
if not isinstance(state, bool):
- raise ValueError("Value out of range")
- return self.set_interface('sg', 'on' if state else 'off')
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_scatter_gather()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('gro', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing scatter-gather settings!')
+ return False
def set_tso(self, state):
"""
@@ -296,40 +282,38 @@ class EthernetIf(Interface):
>>> i.set_tso(False)
"""
if not isinstance(state, bool):
- raise ValueError("Value out of range")
- return self.set_interface('tso', 'on' if state else 'off')
-
- def set_ufo(self, state):
- """
- Enable UDP fragmentation offloading. State can be either True or False.
-
- Example:
- >>> from vyos.ifconfig import EthernetIf
- >>> i = EthernetIf('eth0')
- >>> i.set_udp_offload(True)
- """
- if not isinstance(state, bool):
- raise ValueError("Value out of range")
- return self.set_interface('ufo', 'on' if state else 'off')
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_tcp_segmentation_offload()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('gro', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing tcp-segmentation-offload settings!')
+ return False
- def set_ring_buffer(self, b_type, b_size):
+ def set_ring_buffer(self, rx_tx, size):
"""
Example:
>>> from vyos.ifconfig import EthernetIf
>>> i = EthernetIf('eth0')
>>> i.set_ring_buffer('rx', '4096')
"""
+ current_size = self.ethtool.get_ring_buffer(rx_tx)
+ if current_size == size:
+ # bail out early if nothing is about to change
+ return None
+
ifname = self.config['ifname']
- cmd = f'ethtool -G {ifname} {b_type} {b_size}'
+ cmd = f'ethtool --set-ring {ifname} {rx_tx} {size}'
output, code = self._popen(cmd)
# ethtool error codes:
# 80 - value already setted
# 81 - does not possible to set value
if code and code != 80:
- print(f'could not set "{b_type}" ring-buffer for {ifname}')
+ print(f'could not set "{rx_tx}" ring-buffer for {ifname}')
return output
-
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
@@ -358,9 +342,6 @@ class EthernetIf(Interface):
# TSO (TCP segmentation offloading)
self.set_tso(dict_search('offload.tso', config) != None)
- # UDP fragmentation offloading
- self.set_ufo(dict_search('offload.ufo', config) != None)
-
# Set physical interface speed and duplex
if {'speed', 'duplex'} <= set(config):
speed = config.get('speed')
@@ -369,8 +350,8 @@ class EthernetIf(Interface):
# Set interface ring buffer
if 'ring_buffer' in config:
- for b_type in config['ring_buffer']:
- self.set_ring_buffer(b_type, config['ring_buffer'][b_type])
+ for rx_tx, size in config['ring_buffer'].items():
+ self.set_ring_buffer(rx_tx, size)
# call base class first
super().update(config)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 9c02af68f..036ca1413 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -37,7 +37,9 @@ from vyos.util import mac2eui64
from vyos.util import dict_search
from vyos.util import read_file
from vyos.util import get_interface_config
+from vyos.util import is_systemd_service_active
from vyos.template import is_ipv4
+from vyos.template import is_ipv6
from vyos.validate import is_intf_addr_assigned
from vyos.validate import is_ipv6_link_local
from vyos.validate import assert_boolean
@@ -52,6 +54,9 @@ from vyos.ifconfig.vrrp import VRRP
from vyos.ifconfig.operational import Operational
from vyos.ifconfig import Section
+from netaddr import EUI
+from netaddr import mac_unix_expanded
+
class Interface(Control):
# This is the class which will be used to create
# self.operational, it allows subclasses, such as
@@ -103,6 +108,10 @@ class Interface(Control):
'shellcmd': 'ip -json -detail link list dev {ifname}',
'format': lambda j: jmespath.search('[*].operstate | [0]', json.loads(j)),
},
+ 'vrf': {
+ 'shellcmd': 'ip -json -detail link list dev {ifname}',
+ 'format': lambda j: jmespath.search('[*].master | [0]', json.loads(j)),
+ },
}
_command_set = {
@@ -134,7 +143,6 @@ class Interface(Control):
_sysfs_set = {
'arp_cache_tmo': {
- 'convert': lambda tmo: (int(tmo) * 1000),
'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms',
},
'arp_filter': {
@@ -204,6 +212,51 @@ class Interface(Control):
},
}
+ _sysfs_get = {
+ 'arp_cache_tmo': {
+ 'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms',
+ },
+ 'arp_filter': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_filter',
+ },
+ 'arp_accept': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_accept',
+ },
+ 'arp_announce': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_announce',
+ },
+ 'arp_ignore': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_ignore',
+ },
+ 'ipv4_forwarding': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/forwarding',
+ },
+ 'rp_filter': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/rp_filter',
+ },
+ 'ipv6_accept_ra': {
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_ra',
+ },
+ 'ipv6_autoconf': {
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/autoconf',
+ },
+ 'ipv6_forwarding': {
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/forwarding',
+ },
+ 'ipv6_dad_transmits': {
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
+ },
+ 'proxy_arp': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp',
+ },
+ 'proxy_arp_pvlan': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp_pvlan',
+ },
+ 'link_detect': {
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/link_filter',
+ },
+ }
+
@classmethod
def exists(cls, ifname):
return os.path.exists(f'/sys/class/net/{ifname}')
@@ -354,6 +407,9 @@ class Interface(Control):
>>> Interface('eth0').get_mtu()
'1400'
"""
+ tmp = self.get_interface('mtu')
+ if str(tmp) == mtu:
+ return None
return self.set_interface('mtu', mtu)
def get_mac(self):
@@ -367,6 +423,51 @@ class Interface(Control):
"""
return self.get_interface('mac')
+ def get_mac_synthetic(self):
+ """
+ Get a synthetic MAC address. This is a common method which can be called
+ from derived classes to overwrite the get_mac() call in a generic way.
+
+ NOTE: Tunnel interfaces have no "MAC" address by default. The content
+ of the 'address' file in /sys/class/net/device contains the
+ local-ip thus we generate a random MAC address instead
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_mac()
+ '00:50:ab:cd:ef:00'
+ """
+ from hashlib import sha256
+
+ # Get processor ID number
+ cpu_id = self._cmd('sudo dmidecode -t 4 | grep ID | head -n1 | sed "s/.*ID://;s/ //g"')
+
+ # XXX: T3894 - it seems not all systems have eth0 - get a list of all
+ # available Ethernet interfaces on the system (without VLAN subinterfaces)
+ # and then take the first one.
+ all_eth_ifs = [x for x in Section.interfaces('ethernet') if '.' not in x]
+ first_mac = Interface(all_eth_ifs[0]).get_mac()
+
+ sha = sha256()
+ # Calculate SHA256 sum based on the CPU ID number, eth0 mac address and
+ # this interface identifier - this is as predictable as an interface
+ # MAC address and thus can be used in the same way
+ sha.update(cpu_id.encode())
+ sha.update(first_mac.encode())
+ sha.update(self.ifname.encode())
+ # take the most significant 48 bits from the SHA256 string
+ tmp = sha.hexdigest()[:12]
+ # Convert pseudo random string into EUI format which now represents a
+ # MAC address
+ tmp = EUI(tmp).value
+ # set locally administered bit in MAC address
+ tmp |= 0xf20000000000
+ # convert integer to "real" MAC address representation
+ mac = EUI(hex(tmp).split('x')[-1])
+ # change dialect to use : as delimiter instead of -
+ mac.dialect = mac_unix_expanded
+ return str(mac)
+
def set_mac(self, mac):
"""
Set interface MAC (Media Access Contrl) address to given value.
@@ -391,7 +492,7 @@ class Interface(Control):
if prev_state == 'up':
self.set_admin_state('up')
- def set_vrf(self, vrf=''):
+ def set_vrf(self, vrf):
"""
Add/Remove interface from given VRF instance.
@@ -400,6 +501,11 @@ class Interface(Control):
>>> Interface('eth0').set_vrf('foo')
>>> Interface('eth0').set_vrf()
"""
+
+ tmp = self.get_interface('vrf')
+ if tmp == vrf:
+ return None
+
self.set_interface('vrf', vrf)
def set_arp_cache_tmo(self, tmo):
@@ -411,6 +517,10 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_arp_cache_tmo(40)
"""
+ tmo = str(int(tmo) * 1000)
+ tmp = self.get_interface('arp_cache_tmo')
+ if tmp == tmo:
+ return None
return self.set_interface('arp_cache_tmo', tmo)
def set_arp_filter(self, arp_filter):
@@ -431,6 +541,9 @@ class Interface(Control):
particular interfaces. Only for more complex setups like load-
balancing, does this behaviour cause problems.
"""
+ tmp = self.get_interface('arp_filter')
+ if tmp == arp_filter:
+ return None
return self.set_interface('arp_filter', arp_filter)
def set_arp_accept(self, arp_accept):
@@ -447,6 +560,9 @@ class Interface(Control):
gratuitous arp frame, the arp table will be updated regardless
if this setting is on or off.
"""
+ tmp = self.get_interface('arp_accept')
+ if tmp == arp_accept:
+ return None
return self.set_interface('arp_accept', arp_accept)
def set_arp_announce(self, arp_announce):
@@ -468,6 +584,9 @@ class Interface(Control):
receiving answer from the resolved target while decreasing
the level announces more valid sender's information.
"""
+ tmp = self.get_interface('arp_announce')
+ if tmp == arp_announce:
+ return None
return self.set_interface('arp_announce', arp_announce)
def set_arp_ignore(self, arp_ignore):
@@ -480,12 +599,16 @@ class Interface(Control):
1 - reply only if the target IP address is local address
configured on the incoming interface
"""
+ tmp = self.get_interface('arp_ignore')
+ if tmp == arp_ignore:
+ return None
return self.set_interface('arp_ignore', arp_ignore)
def set_ipv4_forwarding(self, forwarding):
- """
- Configure IPv4 forwarding.
- """
+ """ Configure IPv4 forwarding. """
+ tmp = self.get_interface('ipv4_forwarding')
+ if tmp == forwarding:
+ return None
return self.set_interface('ipv4_forwarding', forwarding)
def set_ipv4_source_validation(self, value):
@@ -514,6 +637,9 @@ class Interface(Control):
print(f'WARNING: Global source-validation is set to "{global_setting}\n"' \
'this overrides per interface setting!')
+ tmp = self.get_interface('rp_filter')
+ if int(tmp) == value:
+ return None
return self.set_interface('rp_filter', value)
def set_ipv6_accept_ra(self, accept_ra):
@@ -529,6 +655,9 @@ class Interface(Control):
2 - Overrule forwarding behaviour. Accept Router Advertisements even if
forwarding is enabled.
"""
+ tmp = self.get_interface('ipv6_accept_ra')
+ if tmp == accept_ra:
+ return None
return self.set_interface('ipv6_accept_ra', accept_ra)
def set_ipv6_autoconf(self, autoconf):
@@ -536,6 +665,9 @@ class Interface(Control):
Autoconfigure addresses using Prefix Information in Router
Advertisements.
"""
+ tmp = self.get_interface('ipv6_autoconf')
+ if tmp == autoconf:
+ return None
return self.set_interface('ipv6_autoconf', autoconf)
def add_ipv6_eui64_address(self, prefix):
@@ -559,9 +691,10 @@ class Interface(Control):
Delete the address based on the interface's MAC-based EUI64
combined with the prefix address.
"""
- eui64 = mac2eui64(self.get_mac(), prefix)
- prefixlen = prefix.split('/')[1]
- self.del_addr(f'{eui64}/{prefixlen}')
+ if is_ipv6(prefix):
+ eui64 = mac2eui64(self.get_mac(), prefix)
+ prefixlen = prefix.split('/')[1]
+ self.del_addr(f'{eui64}/{prefixlen}')
def set_ipv6_forwarding(self, forwarding):
"""
@@ -588,6 +721,9 @@ class Interface(Control):
3. Router Advertisements are ignored unless accept_ra is 2.
4. Redirects are ignored.
"""
+ tmp = self.get_interface('ipv6_forwarding')
+ if tmp == forwarding:
+ return None
return self.set_interface('ipv6_forwarding', forwarding)
def set_ipv6_dad_messages(self, dad):
@@ -595,6 +731,9 @@ class Interface(Control):
The amount of Duplicate Address Detection probes to send.
Default: 1
"""
+ tmp = self.get_interface('ipv6_dad_transmits')
+ if tmp == dad:
+ return None
return self.set_interface('ipv6_dad_transmits', dad)
def set_link_detect(self, link_filter):
@@ -617,6 +756,9 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_link_detect(1)
"""
+ tmp = self.get_interface('link_detect')
+ if tmp == link_filter:
+ return None
return self.set_interface('link_detect', link_filter)
def get_alias(self):
@@ -641,6 +783,9 @@ class Interface(Control):
>>> Interface('eth0').set_ifalias('')
"""
+ tmp = self.get_interface('alias')
+ if tmp == ifalias:
+ return None
self.set_interface('alias', ifalias)
def get_admin_state(self):
@@ -716,6 +861,9 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_proxy_arp(1)
"""
+ tmp = self.get_interface('proxy_arp')
+ if tmp == enable:
+ return None
self.set_interface('proxy_arp', enable)
def set_proxy_arp_pvlan(self, enable):
@@ -742,6 +890,9 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_proxy_arp_pvlan(1)
"""
+ tmp = self.get_interface('proxy_arp_pvlan')
+ if tmp == enable:
+ return None
self.set_interface('proxy_arp_pvlan', enable)
def get_addr_v4(self):
@@ -878,6 +1029,8 @@ class Interface(Control):
>>> j.get_addr()
['2001:db8::ffff/64']
"""
+ if not addr:
+ raise ValueError()
# remove from interface
if addr == 'dhcp':
@@ -984,7 +1137,9 @@ class Interface(Control):
lease_file = f'{config_base}_{ifname}.leases'
# Stop client with old config files to get the right IF_METRIC.
- self._cmd(f'systemctl stop dhclient@{ifname}.service')
+ systemd_service = f'dhclient@{ifname}.service'
+ if is_systemd_service_active(systemd_service):
+ self._cmd(f'systemctl stop {systemd_service}')
if enable and 'disable' not in self._config:
if dict_search('dhcp_options.host_name', self._config) == None:
@@ -1004,7 +1159,7 @@ class Interface(Control):
# 'up' check is mandatory b/c even if the interface is A/D, as soon as
# the DHCP client is started the interface will be placed in u/u state.
# This is not what we intended to do when disabling an interface.
- return self._cmd(f'systemctl start dhclient@{ifname}.service')
+ return self._cmd(f'systemctl restart {systemd_service}')
else:
# cleanup old config files
for file in [config_file, options_file, pid_file, lease_file]:
@@ -1021,17 +1176,18 @@ class Interface(Control):
ifname = self.ifname
config_file = f'/run/dhcp6c/dhcp6c.{ifname}.conf'
+ systemd_service = f'dhcp6c@{ifname}.service'
if enable and 'disable' not in self._config:
render(config_file, 'dhcp-client/ipv6.tmpl',
self._config)
- # We must ignore any return codes. This is required to enable DHCPv6-PD
- # for interfaces which are yet not up and running.
- return self._popen(f'systemctl restart dhcp6c@{ifname}.service')
+ # We must ignore any return codes. This is required to enable
+ # DHCPv6-PD for interfaces which are yet not up and running.
+ return self._popen(f'systemctl restart {systemd_service}')
else:
- self._popen(f'systemctl stop dhcp6c@{ifname}.service')
-
+ if is_systemd_service_active(systemd_service):
+ self._cmd(f'systemctl stop {systemd_service}')
if os.path.isfile(config_file):
os.remove(config_file)
@@ -1048,12 +1204,14 @@ class Interface(Control):
source_if = next(iter(self._config['is_mirror_intf']))
config = self._config['is_mirror_intf'][source_if].get('mirror', None)
- # Please do not clear the 'set $? = 0 '. It's meant to force a return of 0
- # Remove existing mirroring rules
- delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress 2> /dev/null;'
- delete_tc_cmd += f'tc qdisc del dev {source_if} handle 1: root prio 2> /dev/null;'
- delete_tc_cmd += 'set $?=0'
- self._popen(delete_tc_cmd)
+ # Check configuration stored by old perl code before delete T3782
+ if not 'redirect' in self._config:
+ # Please do not clear the 'set $? = 0 '. It's meant to force a return of 0
+ # Remove existing mirroring rules
+ delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress 2> /dev/null;'
+ delete_tc_cmd += f'tc qdisc del dev {source_if} handle 1: root prio 2> /dev/null;'
+ delete_tc_cmd += 'set $?=0'
+ self._popen(delete_tc_cmd)
# Bail out early if nothing needs to be configured
if not config:
@@ -1124,16 +1282,16 @@ class Interface(Control):
# determine IP addresses which are assigned to the interface and build a
# list of addresses which are no longer in the dict so they can be removed
- cur_addr = self.get_addr()
- for addr in list_diff(cur_addr, new_addr):
- # we will delete all interface specific IP addresses if they are not
- # explicitly configured on the CLI
- if is_ipv6_link_local(addr):
- eui64 = mac2eui64(self.get_mac(), 'fe80::/64')
- if addr != f'{eui64}/64':
+ if 'address_old' in config:
+ for addr in list_diff(config['address_old'], new_addr):
+ # we will delete all interface specific IP addresses if they are not
+ # explicitly configured on the CLI
+ if is_ipv6_link_local(addr):
+ eui64 = mac2eui64(self.get_mac(), 'fe80::/64')
+ if addr != f'{eui64}/64':
+ self.del_addr(addr)
+ else:
self.del_addr(addr)
- else:
- self.del_addr(addr)
for addr in new_addr:
self.add_addr(addr)
@@ -1224,16 +1382,11 @@ class Interface(Control):
self.set_mtu(config.get('mtu'))
# Delete old IPv6 EUI64 addresses before changing MAC
- tmp = dict_search('ipv6.address.eui64_old', config)
- if tmp:
- for addr in tmp:
- self.del_ipv6_eui64_address(addr)
+ for addr in (dict_search('ipv6.address.eui64_old', config) or []):
+ self.del_ipv6_eui64_address(addr)
# Manage IPv6 link-local addresses
- 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 isinstance(tmp, dict):
+ if dict_search('ipv6.address.no_default_link_local', config) != None:
self.del_ipv6_eui64_address('fe80::/64')
else:
self.add_ipv6_eui64_address('fe80::/64')
diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py
index 173a90bb4..0e4447b9e 100644
--- a/python/vyos/ifconfig/section.py
+++ b/python/vyos/ifconfig/section.py
@@ -46,7 +46,7 @@ class Section:
return klass
@classmethod
- def _basename (cls, name, vlan):
+ def _basename(cls, name, vlan, vrrp):
"""
remove the number at the end of interface name
name: name of the interface
@@ -56,16 +56,18 @@ class Section:
name = name.rstrip('.')
if vlan:
name = name.rstrip('0123456789.')
+ if vrrp:
+ name = name.rstrip('0123456789v')
return name
@classmethod
- def section(cls, name, vlan=True):
+ def section(cls, name, vlan=True, vrrp=True):
"""
return the name of a section an interface should be under
name: name of the interface (eth0, dum1, ...)
vlan: should we try try to remove the VLAN from the number
"""
- name = cls._basename(name, vlan)
+ name = cls._basename(name, vlan, vrrp)
if name in cls._prefixes:
return cls._prefixes[name].definition['section']
@@ -79,8 +81,8 @@ class Section:
return list(set([cls._prefixes[_].definition['section'] for _ in cls._prefixes]))
@classmethod
- def klass(cls, name, vlan=True):
- name = cls._basename(name, vlan)
+ def klass(cls, name, vlan=True, vrrp=True):
+ name = cls._basename(name, vlan, vrrp)
if name in cls._prefixes:
return cls._prefixes[name]
raise ValueError(f'No type found for interface name: {name}')
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index e40756cc7..5258a2cb1 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -16,10 +16,6 @@
# https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/
# https://community.hetzner.com/tutorials/linux-setup-gre-tunnel
-from netaddr import EUI
-from netaddr import mac_unix_expanded
-from random import getrandbits
-
from vyos.ifconfig.interface import Interface
from vyos.util import dict_search
from vyos.validate import assert_list
@@ -163,26 +159,8 @@ class TunnelIf(Interface):
self._cmd(cmd.format(**self.config))
def get_mac(self):
- """
- Get current interface MAC (Media Access Contrl) address used.
- NOTE: Tunnel interfaces have no "MAC" address by default. The content
- of the 'address' file in /sys/class/net/device contains the
- local-ip thus we generate a random MAC address instead
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').get_mac()
- '00:50:ab:cd:ef:00'
- """
- # we choose 40 random bytes for the MAC address, this gives
- # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A')
- tmp = EUI(getrandbits(48)).value
- # set locally administered bit in MAC address
- tmp |= 0xf20000000000
- # convert integer to "real" MAC address representation
- mac = EUI(hex(tmp).split('x')[-1])
- # change dialect to use : as delimiter instead of -
- mac.dialect = mac_unix_expanded
- return str(mac)
+ """ Get a synthetic MAC address. """
+ return self.get_mac_synthetic()
def update(self, config):
""" General helper function which works on a dictionary retrived by
diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py
index b522cc1ab..3d6f4d7c6 100644
--- a/python/vyos/ifconfig/vrrp.py
+++ b/python/vyos/ifconfig/vrrp.py
@@ -32,14 +32,13 @@ class VRRPNoData(VRRPError):
class VRRP(object):
_vrrp_prefix = '00:00:5E:00:01:'
location = {
- 'pid': '/run/keepalived.pid',
- 'fifo': '/run/keepalived_notify_fifo',
+ 'pid': '/run/keepalived/keepalived.pid',
+ 'fifo': '/run/keepalived/keepalived_notify_fifo',
'state': '/tmp/keepalived.data',
'stats': '/tmp/keepalived.stats',
'json': '/tmp/keepalived.json',
- 'daemon': '/etc/default/keepalived',
- 'config': '/etc/keepalived/keepalived.conf',
- 'vyos': '/run/keepalived_config.dict',
+ 'config': '/run/keepalived/keepalived.conf',
+ 'vyos': '/run/keepalived/keepalived_config.dict',
}
_signal = {
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 2d2243b84..de1b56ce5 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -17,9 +17,6 @@ import os
import time
from datetime import timedelta
-from netaddr import EUI
-from netaddr import mac_unix_expanded
-from random import getrandbits
from hurry.filesize import size
from hurry.filesize import alternative
@@ -163,28 +160,8 @@ class WireGuardIf(Interface):
'allowed_ips', 'fwmark', 'endpoint', 'keepalive']
def get_mac(self):
- """
- Get current interface MAC (Media Access Contrl) address used.
-
- NOTE: Tunnel interfaces have no "MAC" address by default. The content
- of the 'address' file in /sys/class/net/device contains the
- local-ip thus we generate a random MAC address instead
-
- Example:
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').get_mac()
- '00:50:ab:cd:ef:00'
- """
- # we choose 40 random bytes for the MAC address, this gives
- # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A')
- tmp = EUI(getrandbits(48)).value
- # set locally administered bit in MAC address
- tmp |= 0xf20000000000
- # convert integer to "real" MAC address representation
- mac = EUI(hex(tmp).split('x')[-1])
- # change dialect to use : as delimiter instead of -
- mac.dialect = mac_unix_expanded
- return str(mac)
+ """ Get a synthetic MAC address. """
+ return self.get_mac_synthetic()
def update(self, config):
""" General helper function which works on a dictionary retrived by
diff --git a/python/vyos/ifconfig/wwan.py b/python/vyos/ifconfig/wwan.py
index f18959a60..845c9bef9 100644
--- a/python/vyos/ifconfig/wwan.py
+++ b/python/vyos/ifconfig/wwan.py
@@ -26,3 +26,20 @@ class WWANIf(Interface):
'eternal': 'wwan[0-9]+$',
},
}
+
+ def remove(self):
+ """
+ Remove interface from config. Removing the interface deconfigures all
+ assigned IP addresses.
+ Example:
+ >>> from vyos.ifconfig import WWANIf
+ >>> i = WWANIf('wwan0')
+ >>> i.remove()
+ """
+
+ if self.exists(self.ifname):
+ # interface is placed in A/D state when removed from config! It
+ # will remain visible for the operating system.
+ self.set_admin_state('down')
+
+ super().remove()
diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py
index 9a5fdef2f..4574bb6d1 100644
--- a/python/vyos/migrator.py
+++ b/python/vyos/migrator.py
@@ -15,6 +15,7 @@
import sys
import os
+import json
import subprocess
import vyos.version
import vyos.defaults
@@ -165,6 +166,20 @@ class Migrator(object):
versions_string,
os_version_string)
+ def save_json_record(self, component_versions: dict):
+ """
+ Write component versions to a json file
+ """
+ mask = os.umask(0o113)
+ version_file = vyos.defaults.component_version_json
+ try:
+ with open(version_file, 'w') as f:
+ f.write(json.dumps(component_versions, indent=2, sort_keys=True))
+ except OSError:
+ pass
+ finally:
+ os.umask(mask)
+
def run(self):
"""
Gather component versions from config file and system.
@@ -182,6 +197,9 @@ class Migrator(object):
sys_versions = systemversions.get_system_versions()
+ # save system component versions in json file for easy reference
+ self.save_json_record(sys_versions)
+
rev_versions = self.run_migration_scripts(cfg_versions, sys_versions)
if rev_versions != cfg_versions:
diff --git a/python/vyos/systemversions.py b/python/vyos/systemversions.py
index 5c4deca29..9b3f4f413 100644
--- a/python/vyos/systemversions.py
+++ b/python/vyos/systemversions.py
@@ -16,15 +16,12 @@
import os
import re
import sys
-import json
-
import vyos.defaults
def get_system_versions():
"""
- Get component versions from running system: read vyatta directory
- structure for versions, then read vyos JSON file. It is a critical
- error if either migration directory or JSON file is unreadable.
+ Get component versions from running system; critical failure if
+ unable to read migration directory.
"""
system_versions = {}
@@ -39,25 +36,4 @@ def get_system_versions():
pair = info.split('@')
system_versions[pair[0]] = int(pair[1])
- version_dict = {}
- path = vyos.defaults.version_file
-
- if os.path.isfile(path):
- with open(path, 'r') as f:
- try:
- version_dict = json.load(f)
- except ValueError as err:
- print(f"\nValue error in {path}: {err}")
- sys.exit(1)
-
- for k, v in version_dict.items():
- if not isinstance(v, int):
- print(f"\nType error in {path}; expecting Dict[str, int]")
- sys.exit(1)
- existing = system_versions.get(k)
- if existing is None:
- system_versions[k] = v
- elif v > existing:
- system_versions[k] = v
-
return system_versions
diff --git a/python/vyos/template.py b/python/vyos/template.py
index b58f641e1..f9e754357 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -29,13 +29,17 @@ _FILTERS = {}
# reuse Environments with identical settings to improve performance
@functools.lru_cache(maxsize=2)
-def _get_environment():
+def _get_environment(location=None):
+ if location is None:
+ loc_loader=FileSystemLoader(directories["templates"])
+ else:
+ loc_loader=FileSystemLoader(location)
env = Environment(
# Don't check if template files were modified upon re-rendering
auto_reload=False,
# Cache up to this number of templates for quick re-rendering
cache_size=100,
- loader=FileSystemLoader(directories["templates"]),
+ loader=loc_loader,
trim_blocks=True,
)
env.filters.update(_FILTERS)
@@ -63,7 +67,7 @@ def register_filter(name, func=None):
return func
-def render_to_string(template, content, formater=None):
+def render_to_string(template, content, formater=None, location=None):
"""Render a template from the template directory, raise on any errors.
:param template: the path to the template relative to the template folder
@@ -78,7 +82,7 @@ def render_to_string(template, content, formater=None):
package is build (recovering the load time and overhead caused by having the
file out of the code).
"""
- template = _get_environment().get_template(template)
+ template = _get_environment(location).get_template(template)
rendered = template.render(content)
if formater is not None:
rendered = formater(rendered)
@@ -93,6 +97,7 @@ def render(
permission=None,
user=None,
group=None,
+ location=None,
):
"""Render a template from the template directory to a file, raise on any errors.
@@ -109,7 +114,7 @@ def render(
# As we are opening the file with 'w', we are performing the rendering before
# calling open() to not accidentally erase the file if rendering fails
- rendered = render_to_string(template, content, formater)
+ rendered = render_to_string(template, content, formater, location)
# Write to file
with open(destination, "w") as file:
diff --git a/python/vyos/util.py b/python/vyos/util.py
index f3451fd77..1834b78bd 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -676,20 +676,20 @@ def find_device_file(device):
return None
-def dict_search(path, my_dict):
- """ Traverse Python dictionary (my_dict) delimited by dot (.).
+def dict_search(path, dict_object):
+ """ Traverse Python dictionary (dict_object) delimited by dot (.).
Return value of key if found, None otherwise.
- This is faster implementation then jmespath.search('foo.bar', my_dict)"""
- if not isinstance(my_dict, dict) or not path:
+ This is faster implementation then jmespath.search('foo.bar', dict_object)"""
+ if not isinstance(dict_object, dict) or not path:
return None
parts = path.split('.')
inside = parts[:-1]
if not inside:
- if path not in my_dict:
+ if path not in dict_object:
return None
- return my_dict[path]
- c = my_dict
+ return dict_object[path]
+ c = dict_object
for p in parts[:-1]:
c = c.get(p, {})
return c.get(parts[-1], None)
@@ -704,8 +704,32 @@ def get_interface_config(interface):
tmp = loads(cmd(f'ip -d -j link show {interface}'))[0]
return tmp
+def is_systemd_service_active(service):
+ """ Test is a specified systemd service is activated.
+ Returns True if service is active, false otherwise.
+ Copied from: https://unix.stackexchange.com/a/435317 """
+ tmp = cmd(f'systemctl show --value -p ActiveState {service}')
+ return bool((tmp == 'active'))
+
def is_systemd_service_running(service):
""" Test is a specified systemd service is actually running.
- Returns True if service is running, false otherwise. """
- tmp = run(f'systemctl is-active --quiet {service}')
- return bool((tmp == 0))
+ Returns True if service is running, false otherwise.
+ Copied from: https://unix.stackexchange.com/a/435317 """
+ tmp = cmd(f'systemctl show --value -p SubState {service}')
+ return bool((tmp == 'running'))
+
+def is_wwan_connected(interface):
+ """ Determine if a given WWAN interface, e.g. wwan0 is connected to the
+ carrier network or not """
+ import json
+
+ if not interface.startswith('wwan'):
+ raise ValueError(f'Specified interface "{interface}" is not a WWAN interface')
+
+ modem = interface.lstrip('wwan')
+
+ tmp = cmd(f'mmcli --modem {modem} --output-json')
+ tmp = json.loads(tmp)
+
+ # return True/False if interface is in connected state
+ return dict_search('modem.generic.state', tmp) == 'connected'
diff --git a/schema/interface_definition.rnc b/schema/interface_definition.rnc
index d7fc4966c..192a70024 100644
--- a/schema/interface_definition.rnc
+++ b/schema/interface_definition.rnc
@@ -24,16 +24,9 @@
# Interface definition starts with interfaceDefinition tag that may contain node tags
start = element interfaceDefinition
{
- syntaxVersion*,
node*
}
-# interfaceDefinition may contain syntax version attribute lists.
-syntaxVersion = element syntaxVersion
-{
- (componentAttr & versionAttr)
-}
-
# node tag may contain node, leafNode, or tagNode tags
# Those are intermediate configuration nodes that may only contain
# other nodes and must not have values
@@ -109,16 +102,6 @@ properties = element properties
(element keepChildOrder { empty })?
}
-componentAttr = attribute component
-{
- text
-}
-
-versionAttr = attribute version
-{
- text
-}
-
# All nodes must have "name" attribute
nodeNameAttr = attribute name
{
diff --git a/schema/interface_definition.rng b/schema/interface_definition.rng
index 3ff60cf18..1ed18f456 100644
--- a/schema/interface_definition.rng
+++ b/schema/interface_definition.rng
@@ -29,22 +29,10 @@
<start>
<element name="interfaceDefinition">
<zeroOrMore>
- <ref name="syntaxVersion"/>
- </zeroOrMore>
- <zeroOrMore>
<ref name="node"/>
</zeroOrMore>
</element>
</start>
- <!-- interfaceDefinition may contain syntax version attribute lists. -->
- <define name="syntaxVersion">
- <element name="syntaxVersion">
- <interleave>
- <ref name="componentAttr"/>
- <ref name="versionAttr"/>
- </interleave>
- </element>
- </define>
<!--
node tag may contain node, leafNode, or tagNode tags
Those are intermediate configuration nodes that may only contain
@@ -213,12 +201,6 @@
</interleave>
</element>
</define>
- <define name="componentAttr">
- <attribute name="component"/>
- </define>
- <define name="versionAttr">
- <attribute name="version"/>
- </define>
<!-- All nodes must have "name" attribute -->
<define name="nodeNameAttr">
<attribute name="name"/>
diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates
index c285ee594..d4515b8db 100755
--- a/scripts/build-command-op-templates
+++ b/scripts/build-command-op-templates
@@ -29,13 +29,10 @@ import functools
from lxml import etree as ET
# Defaults
-
validator_dir = "/opt/vyatta/libexec/validators"
default_constraint_err_msg = "Invalid value"
-
## Get arguments
-
parser = argparse.ArgumentParser(description='Converts new-style XML interface definitions to old-style command templates')
parser.add_argument('--debug', help='Enable debug information output', action='store_true')
parser.add_argument('INPUT_FILE', type=str, help="XML interface definition file")
@@ -50,11 +47,10 @@ output_dir = args.OUTPUT_DIR
debug = args.debug
## Load and validate the inputs
-
try:
xml = ET.parse(input_file)
except Exception as e:
- print("Failed to load interface definition file {0}".format(input_file))
+ print(f"Failed to load interface definition file {input_file}")
print(e)
sys.exit(1)
@@ -64,19 +60,18 @@ try:
if not validator.validate(xml):
print(validator.error_log)
- print("Interface definition file {0} does not match the schema!".format(input_file))
+ print(f"Interface definition file {input_file} does not match the schema!")
sys.exit(1)
except Exception as e:
- print("Failed to load the XML schema {0}".format(schema_file))
+ print(f"Failed to load the XML schema {schema_file}")
print(e)
sys.exit(1)
if not os.access(output_dir, os.W_OK):
- print("The output directory {0} is not writeable".format(output_dir))
+ print(f"The output directory {output_dir} is not writeable")
sys.exit(1)
## If we got this far, everything must be ok and we can convert the file
-
def make_path(l):
path = functools.reduce(os.path.join, l)
if debug:
@@ -125,21 +120,14 @@ def get_properties(p):
def make_node_def(props, command):
# XXX: replace with a template processor if it grows
# out of control
-
node_def = ""
if "help" in props:
node_def += "help: {0}\n".format(props["help"])
-
-
if "comp_help" in props:
node_def += "allowed: {0}\n".format(props["comp_help"])
-
-
if command is not None:
node_def += "run: {0}\n".format(command.text)
-
-
if debug:
print("The contents of the node.def file:\n", node_def)
@@ -152,7 +140,6 @@ def process_node(n, tmpl_dir):
props_elem = n.find("properties")
children = n.find("children")
command = n.find("command")
-
name = n.get("name")
node_type = n.tag
@@ -160,16 +147,16 @@ def process_node(n, tmpl_dir):
my_tmpl_dir.append(name)
if debug:
- print("Name of the node: {};\n Created directory: ".format(name), end="")
+ print(f"Name of the node: {name};\n Created directory: ", end="")
os.makedirs(make_path(my_tmpl_dir), exist_ok=True)
props = get_properties(props_elem)
+ nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def")
if node_type == "node":
if debug:
- print("Processing node {}".format(name))
+ print(f"Processing node {name}")
- nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def")
# Only create the "node.def" file if it exists but is empty, or if it
# does not exist at all.
if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0:
@@ -180,19 +167,17 @@ def process_node(n, tmpl_dir):
inner_nodes = children.iterfind("*")
for inner_n in inner_nodes:
process_node(inner_n, my_tmpl_dir)
- if node_type == "tagNode":
+ elif node_type == "tagNode":
if debug:
- print("Processing tag node {}".format(name))
+ print(f"Processing tagNode {name}")
os.makedirs(make_path(my_tmpl_dir), exist_ok=True)
- nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def")
- if not os.path.exists(nodedef_path):
+ # Only create the "node.def" file if it exists but is empty, or if it
+ # does not exist at all.
+ if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0:
with open(nodedef_path, "w") as f:
f.write('help: {0}\n'.format(props['help']))
- else:
- # Something has already generated this file
- pass
# Create the inner node.tag part
my_tmpl_dir.append("node.tag")
@@ -201,24 +186,67 @@ def process_node(n, tmpl_dir):
print("Created path for the tagNode: {}".format(make_path(my_tmpl_dir)), end="")
# Not sure if we want partially defined tag nodes, write the file unconditionally
- with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f:
- f.write(make_node_def(props, command))
+ nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def")
+ # Only create the "node.def" file if it exists but is empty, or if it
+ # does not exist at all.
+ if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0:
+ with open(nodedef_path, "w") as f:
+ f.write(make_node_def(props, command))
if children is not None:
inner_nodes = children.iterfind("*")
for inner_n in inner_nodes:
process_node(inner_n, my_tmpl_dir)
- else:
+ elif node_type == "leafNode":
# This is a leaf node
if debug:
- print("Processing leaf node {}".format(name))
-
- with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f:
- f.write(make_node_def(props, command))
+ print(f"Processing leaf node {name}")
+ if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0:
+ with open(nodedef_path, "w") as f:
+ f.write(make_node_def(props, command))
+ else:
+ print(f"Unknown node_type: {node_type}")
+
+
+def get_node_key(node, attr=None):
+ """ Return the sorting key of an xml node using tag and attributes """
+ if attr is None:
+ return '%s' % node.tag + ':'.join([node.get(attr)
+ for attr in sorted(node.attrib)])
+ if attr in node.attrib:
+ return '%s:%s' % (node.tag, node.get(attr))
+ return '%s' % node.tag
+
+
+def sort_children(node, attr=None):
+ """ Sort children along tag and given attribute. if attr is None, sort
+ along all attributes """
+ if not isinstance(node.tag, str): # PYTHON 2: use basestring instead
+ # not a TAG, it is comment or DATA
+ # no need to sort
+ return
+ # sort child along attr
+ node[:] = sorted(node, key=lambda child: get_node_key(child, attr))
+ # and recurse
+ for child in node:
+ sort_children(child, attr)
root = xml.getroot()
+# process_node() processes the XML tree in a fixed order, "node" before "tagNode"
+# before "leafNode". If the generator created a "node.def" file, it can no longer
+# be overwritten - else we would have some stale "node.def" files with an empty
+# help string (T2555). Without the fixed order this would resulted in a case
+# where we get a node and a tagNode with the same name, e.g. "show interfaces
+# ethernet" and "show interfaces ethernet eth0" that the node implementation
+# was not callable from the CLI, rendering this command useless (T3807).
+#
+# This can be fixed by forcing the "node", "tagNode", "leafNode" order by sorting
+# the input XML file automatically (sorting from https://stackoverflow.com/a/46128043)
+# thus adding no additional overhead to the user.
+sort_children(root, 'name')
+
nodes = root.iterfind("*")
for n in nodes:
process_node(n, [output_dir])
diff --git a/scripts/build-command-templates b/scripts/build-command-templates
index d8abb0a13..a0d1015b4 100755
--- a/scripts/build-command-templates
+++ b/scripts/build-command-templates
@@ -320,6 +320,4 @@ root = xml.getroot()
nodes = root.iterfind("*")
for n in nodes:
- if n.tag == "syntaxVersion":
- continue
process_node(n, [output_dir])
diff --git a/scripts/build-component-versions b/scripts/build-component-versions
deleted file mode 100755
index 5362dbdd4..000000000
--- a/scripts/build-component-versions
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import os
-import argparse
-import json
-
-from lxml import etree as ET
-
-parser = argparse.ArgumentParser()
-parser.add_argument('INPUT_DIR', type=str,
- help="Directory containing XML interface definition files")
-parser.add_argument('OUTPUT_DIR', type=str,
- help="Output directory for JSON file")
-
-args = parser.parse_args()
-
-input_dir = args.INPUT_DIR
-output_dir = args.OUTPUT_DIR
-
-version_dict = {}
-
-for filename in os.listdir(input_dir):
- filepath = os.path.join(input_dir, filename)
- print(filepath)
- try:
- xml = ET.parse(filepath)
- except Exception as e:
- print("Failed to load interface definition file {0}".format(filename))
- print(e)
- sys.exit(1)
-
- root = xml.getroot()
- version_data = root.iterfind("syntaxVersion")
- for ver in version_data:
- component = ver.get("component")
- version = int(ver.get("version"))
-
- v = version_dict.get(component)
- if v is None:
- version_dict[component] = version
- elif version > v:
- version_dict[component] = version
-
-out_file = os.path.join(output_dir, 'component-versions.json')
-with open(out_file, 'w') as f:
- json.dump(version_dict, f, indent=4, sort_keys=True)
diff --git a/smoketest/configs/bgp-bfd-communities b/smoketest/configs/bgp-bfd-communities
new file mode 100644
index 000000000..3b3056a51
--- /dev/null
+++ b/smoketest/configs/bgp-bfd-communities
@@ -0,0 +1,533 @@
+interfaces {
+ ethernet eth0 {
+ address 192.0.2.100/25
+ address 2001:db8::ffff/64
+ }
+ loopback lo {
+ }
+}
+policy {
+ large-community-list ANYCAST_ALL {
+ rule 10 {
+ action permit
+ description "Allow all anycast from anywhere"
+ regex "4242420696:100:.*"
+ }
+ }
+ large-community-list ANYCAST_INT {
+ rule 10 {
+ action permit
+ description "Allow all anycast from int"
+ regex 4242420696:100:1
+ }
+ }
+ prefix-list BGP-BACKBONE-IN {
+ description "Inbound backbone routes from other sites"
+ rule 10 {
+ action deny
+ description "Block default route"
+ prefix 0.0.0.0/0
+ }
+ rule 20 {
+ action deny
+ description "Block int primary"
+ ge 21
+ prefix 192.168.0.0/20
+ }
+ rule 30 {
+ action deny
+ description "Block loopbacks"
+ ge 25
+ prefix 192.168.253.0/24
+ }
+ rule 40 {
+ action deny
+ description "Block backbone peering"
+ ge 25
+ prefix 192.168.254.0/24
+ }
+ rule 999 {
+ action permit
+ description "Allow everything else"
+ ge 1
+ prefix 0.0.0.0/0
+ }
+ }
+ prefix-list BGP-BACKBONE-OUT {
+ description "Outbound backbone routes to other sites"
+ rule 10 {
+ action permit
+ description "Int primary"
+ ge 23
+ prefix 192.168.0.0/20
+ }
+ }
+ prefix-list GLOBAL {
+ description "Globally redistributed routes"
+ rule 10 {
+ action permit
+ prefix 192.168.100.1/32
+ }
+ rule 20 {
+ action permit
+ prefix 192.168.7.128/25
+ }
+ }
+ prefix-list6 BGP-BACKBONE-IN-V6 {
+ description "Inbound backbone routes from other sites"
+ rule 10 {
+ action deny
+ description "Block default route"
+ prefix ::/0
+ }
+ rule 20 {
+ action deny
+ description "Block int primary"
+ ge 53
+ prefix fd52:d62e:8011::/52
+ }
+ rule 30 {
+ action deny
+ description "Block peering and stuff"
+ ge 53
+ prefix fd52:d62e:8011:f000::/52
+ }
+ rule 999 {
+ action permit
+ description "Allow everything else"
+ ge 1
+ prefix ::/0
+ }
+ }
+ prefix-list6 BGP-BACKBONE-OUT-V6 {
+ description "Outbound backbone routes to other sites"
+ rule 10 {
+ action permit
+ ge 64
+ prefix fd52:d62e:8011::/52
+ }
+ }
+ prefix-list6 GLOBAL-V6 {
+ description "Globally redistributed routes"
+ rule 10 {
+ action permit
+ ge 64
+ prefix fd52:d62e:8011:2::/63
+ }
+ }
+ route-map BGP-REDISTRIBUTE {
+ rule 10 {
+ action permit
+ description "Prepend AS and allow VPN and modem"
+ match {
+ ip {
+ address {
+ prefix-list GLOBAL
+ }
+ }
+ }
+ set {
+ as-path-prepend 4242420666
+ }
+ }
+ rule 20 {
+ action permit
+ description "Allow VPN"
+ match {
+ ipv6 {
+ address {
+ prefix-list GLOBAL-V6
+ }
+ }
+ }
+ }
+ }
+ route-map BGP-BACKBONE-IN {
+ rule 10 {
+ action permit
+ match {
+ ip {
+ address {
+ prefix-list BGP-BACKBONE-IN
+ }
+ }
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ ipv6 {
+ address {
+ prefix-list BGP-BACKBONE-IN-V6
+ }
+ }
+ }
+ }
+ rule 30 {
+ action permit
+ match {
+ large-community {
+ large-community-list ANYCAST_ALL
+ }
+ }
+ }
+ }
+ route-map BGP-BACKBONE-OUT {
+ rule 10 {
+ action permit
+ match {
+ ip {
+ address {
+ prefix-list BGP-BACKBONE-OUT
+ }
+ }
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ ipv6 {
+ address {
+ prefix-list BGP-BACKBONE-OUT-V6
+ }
+ }
+ }
+ }
+ rule 30 {
+ action permit
+ match {
+ large-community {
+ large-community-list ANYCAST_INT
+ }
+ }
+ set {
+ as-path-prepend 4242420666
+ }
+ }
+ }
+}
+protocols {
+ bfd {
+ peer 192.168.253.1 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address 192.168.253.3
+ }
+ }
+ peer 192.168.253.2 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address 192.168.253.3
+ }
+ }
+ peer 192.168.253.6 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address 192.168.253.3
+ }
+ }
+ peer 192.168.253.7 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address 192.168.253.3
+ }
+ }
+ peer 192.168.253.12 {
+ interval {
+ receive 100
+ transmit 100
+ }
+ multihop
+ source {
+ address 192.168.253.3
+ }
+ }
+ peer fd52:d62e:8011:fffe:192:168:253:1 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address fd52:d62e:8011:fffe:192:168:253:3
+ }
+ }
+ peer fd52:d62e:8011:fffe:192:168:253:2 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address fd52:d62e:8011:fffe:192:168:253:3
+ }
+ }
+ peer fd52:d62e:8011:fffe:192:168:253:6 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address fd52:d62e:8011:fffe:192:168:253:3
+ }
+ }
+ peer fd52:d62e:8011:fffe:192:168:253:7 {
+ interval {
+ receive 50
+ transmit 50
+ }
+ multihop
+ source {
+ address fd52:d62e:8011:fffe:192:168:253:3
+ }
+ }
+ peer fd52:d62e:8011:fffe:192:168:253:12 {
+ interval {
+ receive 100
+ transmit 100
+ }
+ multihop
+ source {
+ address fd52:d62e:8011:fffe:192:168:253:3
+ }
+ }
+ }
+ bgp 4242420666 {
+ address-family {
+ ipv4-unicast {
+ redistribute {
+ connected {
+ route-map BGP-REDISTRIBUTE
+ }
+ static {
+ route-map BGP-REDISTRIBUTE
+ }
+ }
+ }
+ ipv6-unicast {
+ redistribute {
+ connected {
+ route-map BGP-REDISTRIBUTE
+ }
+ }
+ }
+ }
+ neighbor 192.168.253.1 {
+ peer-group INT
+ }
+ neighbor 192.168.253.2 {
+ peer-group INT
+ }
+ neighbor 192.168.253.6 {
+ peer-group DAL13
+ }
+ neighbor 192.168.253.7 {
+ peer-group DAL13
+ }
+ neighbor 192.168.253.12 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export BGP-BACKBONE-OUT
+ import BGP-BACKBONE-IN
+ }
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ bfd {
+ }
+ ebgp-multihop 2
+ remote-as 4242420669
+ update-source dum0
+ }
+ neighbor fd52:d62e:8011:fffe:192:168:253:1 {
+ address-family {
+ ipv6-unicast {
+ peer-group INTv6
+ }
+ }
+ }
+ neighbor fd52:d62e:8011:fffe:192:168:253:2 {
+ address-family {
+ ipv6-unicast {
+ peer-group INTv6
+ }
+ }
+ }
+ neighbor fd52:d62e:8011:fffe:192:168:253:6 {
+ address-family {
+ ipv6-unicast {
+ peer-group DAL13v6
+ }
+ }
+ }
+ neighbor fd52:d62e:8011:fffe:192:168:253:7 {
+ address-family {
+ ipv6-unicast {
+ peer-group DAL13v6
+ }
+ }
+ }
+ neighbor fd52:d62e:8011:fffe:192:168:253:12 {
+ address-family {
+ ipv6-unicast {
+ route-map {
+ export BGP-BACKBONE-OUT
+ import BGP-BACKBONE-IN
+ }
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ bfd {
+ }
+ ebgp-multihop 2
+ remote-as 4242420669
+ update-source dum0
+ }
+ parameters {
+ confederation {
+ identifier 4242420696
+ peers 4242420668
+ peers 4242420669
+ }
+ default {
+ no-ipv4-unicast
+ }
+ distance {
+ global {
+ external 220
+ internal 220
+ local 220
+ }
+ }
+ graceful-restart {
+ }
+ }
+ peer-group DAL13 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export BGP-BACKBONE-OUT
+ import BGP-BACKBONE-IN
+ }
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ bfd
+ ebgp-multihop 2
+ remote-as 4242420668
+ update-source dum0
+ }
+ peer-group DAL13v6 {
+ address-family {
+ ipv6-unicast {
+ route-map {
+ export BGP-BACKBONE-OUT
+ import BGP-BACKBONE-IN
+ }
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ bfd
+ ebgp-multihop 2
+ remote-as 4242420668
+ update-source dum0
+ }
+ peer-group INT {
+ address-family {
+ ipv4-unicast {
+ default-originate {
+ }
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ bfd
+ remote-as 4242420666
+ update-source dum0
+ }
+ peer-group INTv6 {
+ address-family {
+ ipv6-unicast {
+ default-originate {
+ }
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ bfd
+ remote-as 4242420666
+ update-source dum0
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 200
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ level admin
+ }
+ }
+ 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
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+
+/* Warning: Do not remove the following line. */
+/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */
+/* Release version: 1.2.6-S1 */
diff --git a/smoketest/configs/bgp-big-as-cloud b/smoketest/configs/bgp-big-as-cloud
new file mode 100644
index 000000000..694243d1e
--- /dev/null
+++ b/smoketest/configs/bgp-big-as-cloud
@@ -0,0 +1,1956 @@
+firewall {
+ all-ping enable
+ broadcast-ping disable
+ config-trap disable
+ group {
+ address-group bgp-peers-4 {
+ address 192.0.68.3
+ address 192.0.68.2
+ address 192.0.176.193
+ address 192.0.52.0-192.0.52.255
+ address 192.0.53.0-192.0.53.255
+ address 192.0.16.209
+ address 192.0.192.0-192.0.192.255
+ address 192.0.193.0-192.0.193.255
+ address 192.0.194.0-192.0.194.255
+ address 192.0.195.0-192.0.195.255
+ address 192.0.196.0-192.0.196.255
+ address 192.0.197.0-192.0.197.255
+ address 192.0.198.0-192.0.198.255
+ address 192.0.199.0-192.0.199.255
+ }
+ address-group vrrp-peers-4 {
+ address 192.0.68.3
+ address 192.0.160.3
+ address 192.0.98.3
+ address 192.0.71.131
+ address 192.0.84.67
+ address 192.0.71.195
+ address 192.0.71.115
+ address 192.0.70.195
+ address 192.0.70.179
+ address 192.0.70.163
+ address 192.0.70.147
+ address 192.0.70.131
+ address 192.0.70.19
+ address 192.0.70.3
+ address 192.0.71.99
+ address 192.0.68.67
+ address 192.0.71.67
+ address 192.0.71.3
+ address 192.0.68.35
+ address 192.0.68.131
+ address 192.0.69.2
+ address 192.0.70.35
+ address 192.0.70.67
+ }
+ ipv6-address-group bgp-peers-6 {
+ address 2001:db8:c::3
+ address 2001:db8:1000::2e9
+ address 2001:db8:24::fb
+ address 2001:db8:24::fc
+ address 2001:db8:24::fd
+ address 2001:db8:24::2e
+ address 2001:db8:24::3d
+ address 2001:db8:24::4a
+ address 2001:db8:24::5e
+ address 2001:db8:24::7
+ address 2001:db8:24::11
+ address 2001:db8:24::18
+ address 2001:db8:24::20
+ address 2001:db8:24::22
+ address 2001:db8:24::31
+ address 2001:db8:24::58
+ address 2001:db8:24::64
+ address 2001:db8:24::a5
+ address 2001:db8:24::aa
+ address 2001:db8:24::ab
+ address 2001:db8:24::b0
+ address 2001:db8:24::b3
+ address 2001:db8:24::bd
+ address 2001:db8:24::c
+ address 2001:db8:24::d2
+ address 2001:db8:24::d3
+ address 2001:db8:838::1
+ address 2001:db8::1a27:5051:c09d
+ address 2001:db8::1a27:5051:c19d
+ address 2001:db8::20ad:0:1
+ address 2001:db8::2306:0:1
+ address 2001:db8::2ca:0:1
+ address 2001:db8::2ca:0:2
+ address 2001:db8::2ca:0:3
+ address 2001:db8::2ca:0:4
+ }
+ ipv6-address-group vrrp-peers-6 {
+ address fe80::fe89:15cf
+ }
+ ipv6-network-group AS64512-6 {
+ network 2001::/29
+ }
+ network-group AS64512-4 {
+ network 192.0.68.0/22
+ network 192.0.98.0/24
+ network 192.0.160.0/24
+ network 192.0.84.0/22
+ }
+ }
+ ipv6-name management-to-local-6 {
+ default-action reject
+ enable-default-log
+ }
+ ipv6-name management-to-peers-6 {
+ default-action reject
+ enable-default-log
+ }
+ ipv6-name management-to-servers-6 {
+ default-action reject
+ enable-default-log
+ }
+ ipv6-name peers-to-local-6 {
+ default-action reject
+ enable-default-log
+ rule 500 {
+ action accept
+ protocol icmpv6
+ }
+ rule 501 {
+ action accept
+ protocol vrrp
+ source {
+ group {
+ address-group vrrp-peers-6
+ }
+ }
+ }
+ rule 502 {
+ action accept
+ destination {
+ port bgp
+ }
+ protocol tcp
+ source {
+ group {
+ address-group bgp-peers-6
+ }
+ }
+ }
+ rule 503 {
+ action accept
+ protocol tcp
+ source {
+ group {
+ address-group bgp-peers-6
+ }
+ port bgp
+ }
+ }
+ }
+ ipv6-name peers-to-management-6 {
+ default-action reject
+ enable-default-log
+ }
+ ipv6-name peers-to-servers-6 {
+ default-action reject
+ enable-default-log
+ rule 9990 {
+ action reject
+ source {
+ group {
+ network-group AS64512-6
+ }
+ }
+ }
+ rule 9999 {
+ action accept
+ destination {
+ group {
+ network-group AS64512-6
+ }
+ }
+ }
+ }
+ ipv6-name servers-to-local-6 {
+ default-action reject
+ enable-default-log
+ rule 500 {
+ action accept
+ protocol icmpv6
+ }
+ rule 501 {
+ action accept
+ protocol vrrp
+ source {
+ group {
+ address-group vrrp-peers-6
+ }
+ }
+ }
+ rule 511 {
+ action accept
+ protocol tcp_udp
+ source {
+ port 53
+ }
+ }
+ }
+ ipv6-name servers-to-management-6 {
+ default-action reject
+ enable-default-log
+ }
+ ipv6-name servers-to-peers-6 {
+ default-action reject
+ enable-default-log
+ rule 51 {
+ action accept
+ source {
+ group {
+ network-group AS64512-6
+ }
+ }
+ }
+ }
+ ipv6-receive-redirects disable
+ ipv6-src-route disable
+ ip-src-route disable
+ log-martians enable
+ name management-to-local-4 {
+ default-action reject
+ enable-default-log
+ rule 500 {
+ action accept
+ protocol icmp
+ }
+ rule 501 {
+ action accept
+ destination {
+ port 22
+ }
+ protocol tcp
+ }
+ rule 502 {
+ action accept
+ destination {
+ port snmp
+ }
+ protocol udp
+ }
+ }
+ name management-to-peers-4 {
+ default-action reject
+ enable-default-log
+ }
+ name management-to-servers-4 {
+ default-action reject
+ enable-default-log
+ }
+ name peers-to-local-4 {
+ default-action reject
+ enable-default-log
+ rule 500 {
+ action accept
+ protocol icmp
+ }
+ rule 501 {
+ action accept
+ protocol vrrp
+ source {
+ group {
+ address-group vrrp-peers-4
+ }
+ }
+ }
+ rule 502 {
+ action accept
+ destination {
+ port bgp
+ }
+ protocol tcp
+ source {
+ group {
+ address-group bgp-peers-4
+ }
+ }
+ }
+ rule 503 {
+ action accept
+ protocol tcp
+ source {
+ group {
+ address-group bgp-peers-4
+ }
+ port bgp
+ }
+ }
+ }
+ name peers-to-management-4 {
+ default-action reject
+ enable-default-log
+ }
+ name peers-to-servers-4 {
+ default-action reject
+ enable-default-log
+ rule 9990 {
+ action reject
+ source {
+ group {
+ network-group AS64512-4
+ }
+ }
+ }
+ rule 9999 {
+ action accept
+ destination {
+ group {
+ network-group AS64512-4
+ }
+ }
+ }
+ }
+ name servers-to-local-4 {
+ default-action reject
+ enable-default-log
+ rule 500 {
+ action accept
+ protocol icmp
+ }
+ rule 501 {
+ action accept
+ protocol vrrp
+ source {
+ group {
+ address-group vrrp-peers-4
+ }
+ }
+ }
+ rule 511 {
+ action accept
+ protocol tcp_udp
+ source {
+ port 53
+ }
+ }
+ }
+ name servers-to-management-4 {
+ default-action reject
+ enable-default-log
+ }
+ name servers-to-peers-4 {
+ default-action reject
+ enable-default-log
+ rule 51 {
+ action accept
+ source {
+ group {
+ network-group AS64512-4
+ }
+ }
+ }
+ }
+ receive-redirects disable
+ send-redirects enable
+ source-validation disable
+ syn-cookies enable
+ twa-hazards-protection disable
+}
+high-availability {
+ vrrp {
+ group 11-4 {
+ interface eth0.11
+ priority 200
+ virtual-address 192.0.68.1/27
+ vrid 4
+ }
+ group 11-6 {
+ interface eth0.11
+ priority 200
+ virtual-address 2001:db8:c::1/64
+ vrid 6
+ }
+ group 102-4 {
+ interface eth0.102
+ priority 200
+ virtual-address 192.0.98.1/24
+ vrid 4
+ }
+ group 102-6 {
+ interface eth0.102
+ priority 200
+ virtual-address 2001:db8:0:102::1/64
+ vrid 6
+ }
+ group 105-4 {
+ interface eth0.105
+ priority 200
+ virtual-address 192.0.160.1/24
+ vrid 4
+ }
+ group 105-6 {
+ interface eth0.105
+ priority 200
+ virtual-address 2001:db8:0:105::1/64
+ vrid 6
+ }
+ group 1001-4 {
+ interface eth0.1001
+ priority 200
+ virtual-address 192.0.68.33/27
+ vrid 4
+ }
+ group 1001-6 {
+ interface eth0.1001
+ priority 200
+ virtual-address 2001:db8:0:1001::1/64
+ vrid 6
+ }
+ group 1002-4 {
+ interface eth0.1002
+ priority 200
+ virtual-address 192.0.68.65/26
+ vrid 4
+ }
+ group 1002-6 {
+ interface eth0.1002
+ priority 200
+ virtual-address 2001:db8:0:1002::1/64
+ vrid 6
+ }
+ group 1003-4 {
+ interface eth0.1003
+ priority 200
+ virtual-address 192.0.68.129/25
+ vrid 4
+ }
+ group 1003-6 {
+ interface eth0.1003
+ priority 200
+ virtual-address 2001:db8:0:1003::1/64
+ vrid 6
+ }
+ group 1004-4 {
+ interface eth0.1004
+ priority 200
+ virtual-address 192.0.69.1/24
+ vrid 4
+ }
+ group 1004-6 {
+ interface eth0.1004
+ priority 200
+ virtual-address 2001:db8:0:1004::1/64
+ vrid 6
+ }
+ group 1005-4 {
+ interface eth0.1005
+ priority 200
+ virtual-address 192.0.70.1/28
+ vrid 4
+ }
+ group 1005-6 {
+ interface eth0.1005
+ priority 200
+ virtual-address 2001:db8:0:1005::1/64
+ vrid 6
+ }
+ group 1006-4 {
+ interface eth0.1006
+ priority 200
+ virtual-address 192.0.70.17/28
+ vrid 4
+ }
+ group 1006-6 {
+ interface eth0.1006
+ priority 200
+ virtual-address 2001:db8:0:1006::1/64
+ vrid 6
+ }
+ group 1007-4 {
+ interface eth0.1007
+ priority 200
+ virtual-address 192.0.70.33/27
+ vrid 4
+ }
+ group 1007-6 {
+ interface eth0.1007
+ priority 200
+ virtual-address 2001:db8:0:1007::1/64
+ vrid 6
+ }
+ group 1008-4 {
+ interface eth0.1008
+ priority 200
+ virtual-address 192.0.70.65/26
+ vrid 4
+ }
+ group 1008-6 {
+ interface eth0.1008
+ priority 200
+ virtual-address 2001:db8:0:1008::1/64
+ vrid 6
+ }
+ group 1009-4 {
+ interface eth0.1009
+ priority 200
+ virtual-address 192.0.70.129/28
+ vrid 4
+ }
+ group 1009-6 {
+ interface eth0.1009
+ priority 200
+ virtual-address 2001:db8:0:1009::1/64
+ vrid 6
+ }
+ group 1010-4 {
+ interface eth0.1010
+ priority 200
+ virtual-address 192.0.70.145/28
+ vrid 4
+ }
+ group 1010-6 {
+ interface eth0.1010
+ priority 200
+ virtual-address 2001:db8:0:1010::1/64
+ vrid 6
+ }
+ group 1011-4 {
+ interface eth0.1011
+ priority 200
+ virtual-address 192.0.70.161/28
+ vrid 4
+ }
+ group 1011-6 {
+ interface eth0.1011
+ priority 200
+ virtual-address 2001:db8:0:1011::1/64
+ vrid 6
+ }
+ group 1012-4 {
+ interface eth0.1012
+ priority 200
+ virtual-address 192.0.70.177/28
+ vrid 4
+ }
+ group 1012-6 {
+ interface eth0.1012
+ priority 200
+ virtual-address 2001:db8:0:1012::1/64
+ vrid 6
+ }
+ group 1013-4 {
+ interface eth0.1013
+ priority 200
+ virtual-address 192.0.70.193/27
+ vrid 4
+ }
+ group 1013-6 {
+ interface eth0.1013
+ priority 200
+ virtual-address 2001:db8:0:1013::1/64
+ vrid 6
+ }
+ group 1014-4 {
+ interface eth0.1014
+ priority 200
+ virtual-address 192.0.84.65/26
+ vrid 4
+ }
+ group 1014-6 {
+ interface eth0.1014
+ priority 200
+ virtual-address 2001:db8:0:1014::1/64
+ vrid 6
+ }
+ group 1015-4 {
+ interface eth0.1015
+ priority 200
+ virtual-address 192.0.71.1/26
+ vrid 4
+ }
+ group 1015-6 {
+ interface eth0.1015
+ priority 200
+ virtual-address 2001:db8:0:1015::1/64
+ vrid 6
+ }
+ group 1016-4 {
+ interface eth0.1016
+ priority 200
+ virtual-address 192.0.71.65/27
+ vrid 4
+ }
+ group 1016-6 {
+ interface eth0.1016
+ priority 200
+ virtual-address 2001:db8:0:1016::1/64
+ vrid 6
+ }
+ group 1017-4 {
+ interface eth0.1017
+ priority 200
+ virtual-address 192.0.71.97/28
+ vrid 4
+ }
+ group 1017-6 {
+ interface eth0.1017
+ priority 200
+ virtual-address 2001:db8:0:1017::1/64
+ vrid 6
+ }
+ group 1018-4 {
+ interface eth0.1018
+ priority 200
+ virtual-address 192.0.71.113/28
+ vrid 4
+ }
+ group 1018-6 {
+ interface eth0.1018
+ priority 200
+ virtual-address 2001:db8:0:1018::1/64
+ vrid 6
+ }
+ group 1019-4 {
+ interface eth0.1019
+ priority 200
+ virtual-address 192.0.71.129/26
+ vrid 4
+ }
+ group 1019-6 {
+ interface eth0.1019
+ priority 200
+ virtual-address 2001:db8:0:1019::1/64
+ vrid 6
+ }
+ group 1020-4 {
+ interface eth0.1020
+ priority 200
+ virtual-address 192.0.71.193/26
+ vrid 4
+ }
+ group 1020-6 {
+ interface eth0.1020
+ priority 200
+ virtual-address 2001:db8:0:1020::1/64
+ vrid 6
+ }
+ }
+}
+interfaces {
+ ethernet eth0 {
+ address 192.0.0.11/16
+ duplex auto
+ smp-affinity auto
+ speed auto
+ vif 11 {
+ address 192.0.68.2/27
+ address 2001:db8:c::2/64
+ }
+ vif 102 {
+ address 192.0.98.2/24
+ address 2001:db8:0:102::2/64
+ }
+ vif 105 {
+ address 192.0.160.2/24
+ address 2001:db8:0:105::2/64
+ }
+ vif 838 {
+ address 192.0.16.210/30
+ address 2001:db8:838::2/64
+ }
+ vif 886 {
+ address 192.0.193.224/21
+ address 2001:db8::3:669:0:1/64
+ }
+ vif 1001 {
+ address 192.0.68.34/27
+ address 2001:db8:0:1001::2/64
+ }
+ vif 1002 {
+ address 192.0.68.66/26
+ address 2001:db8:0:1002::2/64
+ }
+ vif 1003 {
+ address 192.0.68.130/25
+ address 2001:db8:0:1003::2/64
+ }
+ vif 1004 {
+ address 192.0.69.2/24
+ address 2001:db8:0:1004::2/64
+ }
+ vif 1005 {
+ address 192.0.70.2/28
+ address 2001:db8:0:1005::2/64
+ }
+ vif 1006 {
+ address 192.0.70.18/28
+ address 2001:db8:0:1006::2/64
+ }
+ vif 1007 {
+ address 192.0.70.34/27
+ address 2001:db8:0:1007::2/64
+ }
+ vif 1008 {
+ address 192.0.70.66/26
+ address 2001:db8:0:1008::2/64
+ }
+ vif 1009 {
+ address 192.0.70.130/28
+ address 2001:db8:0:1009::2/64
+ }
+ vif 1010 {
+ address 192.0.70.146/28
+ address 2001:db8:0:1010::2/64
+ }
+ vif 1011 {
+ address 192.0.70.162/28
+ address 2001:db8:0:1011::2/64
+ }
+ vif 1012 {
+ address 192.0.70.178/28
+ address 2001:db8:0:1012::2/64
+ }
+ vif 1013 {
+ address 192.0.70.194/27
+ address 2001:db8:0:1013::3/64
+ }
+ vif 1014 {
+ address 192.0.84.66/26
+ address 2001:db8:0:1014::2/64
+ }
+ vif 1015 {
+ address 192.0.71.2/26
+ address 2001:db8:0:1015::2/64
+ }
+ vif 1016 {
+ address 192.0.71.66/27
+ address 2001:db8:0:1016::2/64
+ }
+ vif 1017 {
+ address 192.0.71.98/28
+ address 2001:db8:0:1017::2/64
+ }
+ vif 1018 {
+ address 192.0.71.114/28
+ address 2001:db8:0:1018::2/64
+ }
+ vif 1019 {
+ address 192.0.71.130/26
+ address 2001:db8:0:1019::2/64
+ }
+ vif 1020 {
+ address 192.0.71.194/26
+ address 2001:db8:0:1020::2/64
+ }
+ vif 4088 {
+ address 2001:db8:24::c7/64
+ address 192.0.52.199/23
+ }
+ vif 4089 {
+ address 192.0.176.194/30
+ address 2001:db8:1000::2ea/126
+ }
+ }
+ loopback lo {
+ }
+}
+policy {
+ as-path-list AS64513-AS64514 {
+ rule 10 {
+ action permit
+ regex "^64513 64514$"
+ }
+ }
+ as-path-list AS64512 {
+ rule 10 {
+ action permit
+ regex ^$
+ }
+ }
+ prefix-list defaultV4 {
+ rule 10 {
+ action permit
+ prefix 0.0.0.0/0
+ }
+ }
+ prefix-list hostrouteV4 {
+ rule 10 {
+ action permit
+ ge 32
+ prefix 192.0.160.0/24
+ }
+ rule 20 {
+ action permit
+ ge 32
+ prefix 192.0.98.0/24
+ }
+ rule 30 {
+ action permit
+ ge 32
+ prefix 192.0.68.0/22
+ }
+ rule 40 {
+ action permit
+ ge 32
+ prefix 192.0.84.0/22
+ }
+ }
+ prefix-list vyosV4 {
+ rule 10 {
+ action permit
+ prefix 192.0.160.0/24
+ }
+ rule 20 {
+ action permit
+ prefix 192.0.98.0/24
+ }
+ rule 30 {
+ action permit
+ prefix 192.0.68.0/22
+ }
+ rule 40 {
+ action permit
+ prefix 192.0.84.0/22
+ }
+ }
+ prefix-list privateV4 {
+ rule 10 {
+ action permit
+ le 32
+ prefix 192.0.0.0/8
+ }
+ rule 20 {
+ action permit
+ le 32
+ prefix 192.0.0.0/12
+ }
+ rule 30 {
+ action permit
+ le 32
+ prefix 192.0.0.0/16
+ }
+ }
+ prefix-list6 all6 {
+ rule 10 {
+ action permit
+ ge 4
+ prefix 2000::/3
+ }
+ }
+ prefix-list6 hostrouteV6 {
+ rule 20 {
+ action permit
+ ge 128
+ prefix 2001:db8::/29
+ }
+ }
+ prefix-list6 vyosV6 {
+ rule 20 {
+ action permit
+ prefix 2001:db8::/29
+ }
+ }
+ prefix-list6 privateV6 {
+ rule 10 {
+ action permit
+ prefix fc00::/7
+ }
+ }
+ route-map ExportRouteMap {
+ rule 5 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list hostrouteV4
+ }
+ }
+ }
+ set {
+ community 65000:666
+ }
+ }
+ rule 10 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list vyosV4
+ }
+ }
+ }
+ }
+ rule 15 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list hostrouteV6
+ }
+ }
+ }
+ set {
+ community 65000:666
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list vyosV6
+ }
+ }
+ }
+ }
+ rule 100 {
+ action deny
+ }
+ }
+ route-map ExportRouteMapAS64515 {
+ rule 10 {
+ action permit
+ match {
+ ipv6 {
+ address {
+ prefix-list all6
+ }
+ }
+ }
+ }
+ rule 20 {
+ action deny
+ match {
+ ip {
+ address {
+ prefix-list defaultV4
+ }
+ }
+ }
+ }
+ rule 100 {
+ action deny
+ }
+ }
+ route-map ExportRouteMapAS64516 {
+ rule 5 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list hostrouteV4
+ }
+ }
+ }
+ set {
+ community 65000:666
+ }
+ }
+ rule 10 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list vyosV4
+ }
+ }
+ }
+ }
+ rule 15 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list hostrouteV6
+ }
+ }
+ }
+ set {
+ community 65000:666
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list vyosV6
+ }
+ }
+ }
+ }
+ rule 100 {
+ action deny
+ }
+ }
+ route-map ExportRouteMapAS64517 {
+ rule 5 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list hostrouteV4
+ }
+ }
+ }
+ set {
+ community 64517:666
+ }
+ }
+ rule 10 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list vyosV4
+ }
+ }
+ }
+ }
+ rule 15 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list hostrouteV6
+ }
+ }
+ }
+ set {
+ community 64517:666
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list vyosV6
+ }
+ }
+ }
+ }
+ rule 100 {
+ action deny
+ }
+ }
+ route-map ExportRouteMapAS64513 {
+ rule 5 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list hostrouteV4
+ }
+ }
+ }
+ set {
+ community 64513:666
+ }
+ }
+ rule 10 {
+ action permit
+ match {
+ as-path AS64512
+ ip {
+ address {
+ prefix-list vyosV4
+ }
+ }
+ }
+ }
+ rule 15 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list hostrouteV6
+ }
+ }
+ }
+ set {
+ community 64513:666
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ as-path AS64512
+ ipv6 {
+ address {
+ prefix-list vyosV6
+ }
+ }
+ }
+ }
+ rule 100 {
+ action deny
+ }
+ }
+ route-map ImportRouteMap {
+ rule 10 {
+ action deny
+ match {
+ ip {
+ address {
+ prefix-list privateV4
+ }
+ }
+ }
+ }
+ rule 15 {
+ action deny
+ match {
+ ipv6 {
+ address {
+ prefix-list privateV6
+ }
+ }
+ }
+ }
+ rule 20 {
+ action deny
+ match {
+ ip {
+ address {
+ prefix-list vyosV4
+ }
+ }
+ }
+ }
+ rule 30 {
+ action deny
+ match {
+ ipv6 {
+ address {
+ prefix-list vyosV6
+ }
+ }
+ }
+ }
+ rule 40 {
+ action deny
+ match {
+ as-path AS64512
+ }
+ }
+ rule 50 {
+ action permit
+ match {
+ as-path AS64513-AS64514
+ }
+ set {
+ weight 10001
+ }
+ }
+ rule 65535 {
+ action permit
+ }
+ }
+}
+protocols {
+ bgp 64500 {
+ address-family {
+ ipv4-unicast {
+ network 192.0.98.0/24 {
+ }
+ network 192.0.160.0/24 {
+ }
+ network 192.0.68.0/22 {
+ }
+ network 192.0.84.0/22 {
+ }
+ redistribute {
+ static {
+ route-map ExportRouteMap
+ }
+ }
+ }
+ ipv6-unicast {
+ network 2001:db8::/29 {
+ }
+ redistribute {
+ static {
+ route-map ExportRouteMap
+ }
+ }
+ }
+ }
+ maximum-paths {
+ ebgp 8
+ ibgp 16
+ }
+ neighbor 192.0.16.209 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export ExportRouteMapAS64516
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64501
+ }
+ neighbor 192.0.192.6 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 100
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64502
+ }
+ neighbor 192.0.192.157 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 350000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64503
+ }
+ neighbor 192.0.192.228 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64504
+ }
+ neighbor 192.0.193.157 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 350000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64505
+ }
+ neighbor 192.0.193.202 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64506
+ }
+ neighbor 192.0.193.223 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64507
+ }
+ neighbor 192.0.194.161 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64508
+ }
+ neighbor 192.0.194.171 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64509
+ }
+ neighbor 192.0.176.193 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export ExportRouteMapAS64516
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64510
+ }
+ neighbor 192.0.52.12 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 300
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64511
+ }
+ neighbor 192.0.52.17 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 75
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password vyosvyos
+ remote-as 64512
+ }
+ neighbor 192.0.52.24 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 300
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64513
+ }
+ neighbor 192.0.52.32 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 50
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password vyosfoooo
+ remote-as 64514
+ }
+ neighbor 192.0.52.34 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64515
+ }
+ neighbor 192.0.52.46 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64516
+ }
+ neighbor 192.0.52.49 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 75
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password secret
+ remote-as 64517
+ }
+ neighbor 192.0.52.74 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 15000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password secretvyos
+ remote-as 64518
+ }
+ neighbor 192.0.52.94 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 250
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64519
+ }
+ neighbor 192.0.52.100 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 50
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64520
+ }
+ neighbor 192.0.52.119 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 30
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64521
+ }
+ neighbor 192.0.52.165 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 50
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64522
+ }
+ neighbor 192.0.52.170 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 150000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64523
+ }
+ neighbor 192.0.52.171 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 10000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64524
+ }
+ neighbor 192.0.52.179 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 20
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64525
+ }
+ neighbor 192.0.52.189 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 1000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64526
+ }
+ neighbor 192.0.52.210 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 15
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64527
+ }
+ neighbor 192.0.52.211 {
+ address-family {
+ ipv4-unicast {
+ maximum-prefix 15
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64528
+ }
+ neighbor 192.0.52.251 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ weight 1010
+ }
+ }
+ remote-as 64529
+ }
+ neighbor 192.0.52.252 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export ExportRouteMap
+ }
+ weight 1010
+ }
+ }
+ remote-as 64530
+ }
+ neighbor 192.0.52.253 {
+ address-family {
+ ipv4-unicast {
+ route-map {
+ export ExportRouteMapAS64515
+ import ImportRouteMap
+ }
+ }
+ }
+ passive
+ remote-as 64531
+ }
+ neighbor 192.0.68.3 {
+ address-family {
+ ipv4-unicast {
+ nexthop-self
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ remote-as 64532
+ update-source 192.0.68.2
+ }
+ neighbor 2001:db8:838::1 {
+ address-family {
+ ipv6-unicast {
+ route-map {
+ export ExportRouteMapAS64516
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64533
+ }
+ neighbor 2001:db8:c::3 {
+ address-family {
+ ipv6-unicast {
+ nexthop-self
+ soft-reconfiguration {
+ inbound
+ }
+ }
+ }
+ remote-as 64534
+ update-source 2001:db8:c::2
+ }
+ neighbor 2001:db8:24::2e {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 5
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password vyossecret
+ remote-as 64535
+ }
+ neighbor 2001:db8:24::4a {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 1000
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64536
+ }
+ neighbor 2001:db8:24::5e {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 200
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64537
+ }
+ neighbor 2001:db8:24::11 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 20
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64538
+ }
+ neighbor 2001:db8:24::18 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 300
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64539
+ }
+ neighbor 2001:db8:24::20 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 10
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64540
+ }
+ neighbor 2001:db8:24::22 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 5
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64541
+ }
+ neighbor 2001:db8:24::31 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 20
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64542
+ }
+ neighbor 2001:db8:24::58 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 15
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64543
+ }
+ neighbor 2001:db8:24::64 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 10
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password geheim
+ remote-as 64544
+ }
+ neighbor 2001:db8:24::a5 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 10
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64545
+ }
+ neighbor 2001:db8:24::aa {
+ address-family {
+ ipv6-unicast {
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64546
+ }
+ neighbor 2001:db8:24::ab {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 1800
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ remote-as 64547
+ }
+ neighbor 2001:db8:24::b0 {
+ address-family {
+ ipv6-unicast {
+ maximum-prefix 5
+ route-map {
+ export ExportRouteMap
+ import ImportRouteMap
+ }
+ }
+ }
+ password secret123
+ remote-as 64548
+ }
+ parameters {
+ default {
+ no-ipv4-unicast
+ }
+ log-neighbor-changes
+ router-id 192.0.68.2
+ }
+ }
+ static {
+ route 192.0.98.0/24 {
+ blackhole {
+ }
+ }
+ route 192.0.160.0/24 {
+ blackhole {
+ }
+ }
+ route 192.0.68.0/22 {
+ blackhole {
+ }
+ }
+ route 192.0.84.0/22 {
+ blackhole {
+ }
+ }
+ route6 2001:db8::/29 {
+ blackhole {
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ flow-accounting {
+ disable-imt
+ interface eth0.4088
+ interface eth0.4089
+ netflow {
+ engine-id 1
+ server 192.0.2.55 {
+ port 2055
+ }
+ version 9
+ }
+ syslog-facility daemon
+ }
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 2001:db8::1
+ name-server 2001:db8::2
+ name-server 192.0.2.1
+ name-server 192.0.2.2
+ ntp {
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level all
+ }
+ preserve-fqdn
+ }
+ }
+ time-zone Europe/Zurich
+}
+zone-policy {
+ zone local {
+ default-action drop
+ from management {
+ firewall {
+ ipv6-name management-to-local-6
+ name management-to-local-4
+ }
+ }
+ from peers {
+ firewall {
+ ipv6-name peers-to-local-6
+ name peers-to-local-4
+ }
+ }
+ from servers {
+ firewall {
+ ipv6-name servers-to-local-6
+ name servers-to-local-4
+ }
+ }
+ local-zone
+ }
+ zone management {
+ default-action reject
+ from peers {
+ firewall {
+ ipv6-name peers-to-management-6
+ name peers-to-management-4
+ }
+ }
+ from servers {
+ firewall {
+ ipv6-name servers-to-management-6
+ name servers-to-management-4
+ }
+ }
+ interface eth0
+ }
+ zone peers {
+ default-action reject
+ from management {
+ firewall {
+ ipv6-name management-to-peers-6
+ name management-to-peers-4
+ }
+ }
+ from servers {
+ firewall {
+ ipv6-name servers-to-peers-6
+ name servers-to-peers-4
+ }
+ }
+ interface eth0.4088
+ interface eth0.4089
+ interface eth0.11
+ interface eth0.838
+ interface eth0.886
+ }
+ zone servers {
+ default-action reject
+ from management {
+ firewall {
+ ipv6-name management-to-servers-6
+ name management-to-servers-4
+ }
+ }
+ from peers {
+ firewall {
+ ipv6-name peers-to-servers-6
+ name peers-to-servers-4
+ }
+ }
+ interface eth0.1001
+ interface eth0.105
+ interface eth0.102
+ interface eth0.1019
+ interface eth0.1014
+ interface eth0.1020
+ interface eth0.1018
+ interface eth0.1013
+ interface eth0.1012
+ interface eth0.1011
+ interface eth0.1010
+ interface eth0.1009
+ interface eth0.1006
+ interface eth0.1005
+ interface eth0.1017
+ interface eth0.1016
+ interface eth0.1002
+ interface eth0.1015
+ interface eth0.1003
+ interface eth0.1004
+ interface eth0.1007
+ interface eth0.1008
+ }
+}
+
+
+/* Warning: Do not remove the following line. */
+/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */
+/* Release version: 1.2.5 */
diff --git a/smoketest/configs/bgp-dmvpn-hub b/smoketest/configs/bgp-dmvpn-hub
new file mode 100644
index 000000000..fc5aadd8f
--- /dev/null
+++ b/smoketest/configs/bgp-dmvpn-hub
@@ -0,0 +1,174 @@
+interfaces {
+ ethernet eth0 {
+ address 100.64.10.1/31
+ }
+ ethernet eth1 {
+ }
+ loopback lo {
+ }
+ tunnel tun0 {
+ address 192.168.254.62/26
+ encapsulation gre
+ multicast enable
+ parameters {
+ ip {
+ key 1
+ }
+ }
+ source-address 100.64.10.1
+ }
+}
+protocols {
+ bgp 65000 {
+ address-family {
+ ipv4-unicast {
+ network 172.20.0.0/16 {
+ }
+ }
+ }
+ neighbor 192.168.254.1 {
+ peer-group DMVPN
+ remote-as 65001
+ }
+ neighbor 192.168.254.2 {
+ peer-group DMVPN
+ remote-as 65002
+ }
+ neighbor 192.168.254.3 {
+ peer-group DMVPN
+ remote-as 65003
+ }
+ parameters {
+ default {
+ no-ipv4-unicast
+ }
+ log-neighbor-changes
+ }
+ peer-group DMVPN {
+ address-family {
+ ipv4-unicast {
+ }
+ }
+ }
+ timers {
+ holdtime 30
+ keepalive 10
+ }
+ }
+ nhrp {
+ tunnel tun0 {
+ cisco-authentication secret
+ holding-time 300
+ multicast dynamic
+ redirect
+ shortcut
+ }
+ }
+ static {
+ route 0.0.0.0/0 {
+ next-hop 100.64.10.0 {
+ }
+ }
+ route 172.20.0.0/16 {
+ blackhole {
+ distance 200
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ conntrack {
+ modules {
+ ftp
+ h323
+ nfs
+ pptp
+ sip
+ sqlnet
+ tftp
+ }
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name cpe-4
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$r/Yw/07NXNY$/ZB.Rjf9jxEV.BYoDyLdH.kH14rU52pOBtrX.4S34qlPt77chflCHvpTCq9a6huLzwaMR50rEICzA5GoIRZlM0
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 1.1.1.1
+ name-server 8.8.8.8
+ name-server 9.9.9.9
+ ntp {
+ server time1.vyos.net {
+ }
+ server time2.vyos.net {
+ }
+ server time3.vyos.net {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+vpn {
+ ipsec {
+ esp-group ESP-DMVPN {
+ compression disable
+ lifetime 1800
+ mode transport
+ pfs dh-group2
+ proposal 1 {
+ encryption aes256
+ hash sha1
+ }
+ }
+ ike-group IKE-DMVPN {
+ close-action none
+ ikev2-reauth no
+ key-exchange ikev1
+ lifetime 3600
+ proposal 1 {
+ dh-group 2
+ encryption aes256
+ hash sha1
+ }
+ }
+ ipsec-interfaces {
+ interface eth0
+ }
+ profile NHRPVPN {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret VyOS-topsecret
+ }
+ bind {
+ tunnel tun0
+ }
+ esp-group ESP-DMVPN
+ ike-group IKE-DMVPN
+ }
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3.0-epa3
+
diff --git a/smoketest/configs/bgp-dmvpn-spoke b/smoketest/configs/bgp-dmvpn-spoke
new file mode 100644
index 000000000..3d7503a9b
--- /dev/null
+++ b/smoketest/configs/bgp-dmvpn-spoke
@@ -0,0 +1,201 @@
+interfaces {
+ ethernet eth0 {
+ vif 7 {
+ description PPPoE-UPLINK
+ }
+ }
+ ethernet eth1 {
+ address 172.17.1.1/24
+ }
+ loopback lo {
+ }
+ pppoe pppoe1 {
+ authentication {
+ password cpe-1
+ user cpe-1
+ }
+ no-peer-dns
+ source-interface eth0.7
+ }
+ tunnel tun0 {
+ address 192.168.254.1/26
+ encapsulation gre
+ multicast enable
+ parameters {
+ ip {
+ key 1
+ }
+ }
+ source-address 0.0.0.0
+ }
+}
+nat {
+ source {
+ rule 10 {
+ log enable
+ outbound-interface pppoe1
+ source {
+ address 172.17.0.0/16
+ }
+ translation {
+ address masquerade
+ }
+ }
+ }
+}
+protocols {
+ bgp 65001 {
+ address-family {
+ ipv4-unicast {
+ network 172.17.0.0/16 {
+ }
+ }
+ }
+ neighbor 192.168.254.62 {
+ address-family {
+ ipv4-unicast {
+ }
+ }
+ remote-as 65000
+ }
+ parameters {
+ default {
+ no-ipv4-unicast
+ }
+ log-neighbor-changes
+ }
+ timers {
+ holdtime 30
+ keepalive 10
+ }
+ }
+ nhrp {
+ tunnel tun0 {
+ cisco-authentication secret
+ holding-time 300
+ map 192.168.254.62/26 {
+ nbma-address 100.64.10.1
+ register
+ }
+ multicast nhs
+ redirect
+ shortcut
+ }
+ }
+ static {
+ route 172.17.0.0/16 {
+ blackhole {
+ distance 200
+ }
+ }
+ }
+}
+service {
+ dhcp-server {
+ shared-network-name LAN-3 {
+ subnet 172.17.1.0/24 {
+ default-router 172.17.1.1
+ name-server 172.17.1.1
+ range 0 {
+ start 172.17.1.100
+ stop 172.17.1.200
+ }
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ conntrack {
+ modules {
+ ftp
+ h323
+ nfs
+ pptp
+ sip
+ sqlnet
+ tftp
+ }
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name cpe-1
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$r/Yw/07NXNY$/ZB.Rjf9jxEV.BYoDyLdH.kH14rU52pOBtrX.4S34qlPt77chflCHvpTCq9a6huLzwaMR50rEICzA5GoIRZlM0
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 1.1.1.1
+ name-server 8.8.8.8
+ name-server 9.9.9.9
+ ntp {
+ server time1.vyos.net {
+ }
+ server time2.vyos.net {
+ }
+ server time3.vyos.net {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+vpn {
+ ipsec {
+ esp-group ESP-DMVPN {
+ compression disable
+ lifetime 1800
+ mode transport
+ pfs dh-group2
+ proposal 1 {
+ encryption aes256
+ hash sha1
+ }
+ }
+ ike-group IKE-DMVPN {
+ close-action none
+ ikev2-reauth no
+ key-exchange ikev1
+ lifetime 3600
+ proposal 1 {
+ dh-group 2
+ encryption aes256
+ hash sha1
+ }
+ }
+ ipsec-interfaces {
+ interface pppoe1
+ }
+ profile NHRPVPN {
+ authentication {
+ mode pre-shared-secret
+ pre-shared-secret VyOS-topsecret
+ }
+ bind {
+ tunnel tun0
+ }
+ esp-group ESP-DMVPN
+ ike-group IKE-DMVPN
+ }
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3.0-epa3
diff --git a/smoketest/configs/dialup-router-complex b/smoketest/configs/dialup-router-complex
new file mode 100644
index 000000000..fef79ea56
--- /dev/null
+++ b/smoketest/configs/dialup-router-complex
@@ -0,0 +1,1662 @@
+firewall {
+ all-ping enable
+ broadcast-ping disable
+ config-trap disable
+ group {
+ address-group MEDIA-STREAMING-CLIENTS {
+ address 172.16.35.241
+ address 172.16.35.242
+ address 172.16.35.243
+ }
+ address-group DMZ-WEBSERVER {
+ address 172.16.36.10
+ address 172.16.36.40
+ address 172.16.36.20
+ }
+ address-group DMZ-RDP-SERVER {
+ address 172.16.33.40
+ }
+ address-group DOMAIN-CONTROLLER {
+ address 172.16.100.10
+ address 172.16.100.20
+ }
+ address-group AUDIO-STREAM {
+ address 172.16.35.20
+ address 172.16.35.21
+ address 172.16.35.22
+ address 172.16.35.23
+ }
+ ipv6-network-group LOCAL-ADDRESSES {
+ network ff02::/64
+ network fe80::/10
+ }
+ network-group SSH-IN-ALLOW {
+ network 192.0.2.0/24
+ network 10.0.0.0/8
+ network 172.16.0.0/12
+ network 192.168.0.0/16
+ }
+ port-group SMART-TV-PORTS {
+ port 5005-5006
+ port 80
+ port 443
+ port 3722
+ }
+ }
+ ipv6-name ALLOW-ALL-6 {
+ default-action accept
+ }
+ ipv6-name ALLOW-BASIC-6 {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ protocol icmpv6
+ }
+ }
+ ipv6-name ALLOW-ESTABLISHED-6 {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ destination {
+ group {
+ network-group LOCAL-ADDRESSES
+ }
+ }
+ protocol icmpv6
+ source {
+ address fe80::/10
+ }
+ }
+ rule 20 {
+ action accept
+ icmpv6 {
+ type echo-request
+ }
+ protocol icmpv6
+ }
+ rule 21 {
+ action accept
+ icmpv6 {
+ type destination-unreachable
+ }
+ protocol icmpv6
+ }
+ rule 22 {
+ action accept
+ icmpv6 {
+ type packet-too-big
+ }
+ protocol icmpv6
+ }
+ rule 23 {
+ action accept
+ icmpv6 {
+ type time-exceeded
+ }
+ protocol icmpv6
+ }
+ rule 24 {
+ action accept
+ icmpv6 {
+ type parameter-problem
+ }
+ protocol icmpv6
+ }
+ }
+ ipv6-name WAN-LOCAL-6 {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ destination {
+ address ff02::/64
+ }
+ protocol icmpv6
+ source {
+ address fe80::/10
+ }
+ }
+ rule 50 {
+ action accept
+ description DHCPv6
+ destination {
+ address fe80::/10
+ port 546
+ }
+ protocol udp
+ source {
+ address fe80::/10
+ port 547
+ }
+ }
+ }
+ ipv6-receive-redirects disable
+ ipv6-src-route disable
+ ip-src-route disable
+ log-martians enable
+ name DMZ-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name DMZ-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "NTP and LDAP to AD DC"
+ destination {
+ group {
+ address-group DOMAIN-CONTROLLER
+ }
+ port 123,389,636
+ }
+ protocol tcp_udp
+ }
+ rule 300 {
+ action accept
+ destination {
+ group {
+ address-group DMZ-RDP-SERVER
+ }
+ port 3389
+ }
+ protocol tcp_udp
+ source {
+ address 172.16.36.20
+ }
+ }
+ }
+ name DMZ-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 50 {
+ action accept
+ destination {
+ address 172.16.254.30
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 123 {
+ action accept
+ destination {
+ port 123
+ }
+ protocol udp
+ }
+ }
+ name DMZ-WAN {
+ default-action accept
+ }
+ name GUEST-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ destination {
+ port 80,443
+ }
+ protocol tcp
+ }
+ }
+ name GUEST-IOT {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "MEDIA-STREAMING-CLIENTS Devices to GUEST"
+ destination {
+ group {
+ address-group MEDIA-STREAMING-CLIENTS
+ }
+ }
+ protocol tcp_udp
+ }
+ rule 110 {
+ action accept
+ description "AUDIO-STREAM Devices to GUEST"
+ destination {
+ group {
+ address-group AUDIO-STREAM
+ }
+ }
+ protocol tcp_udp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name GUEST-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name GUEST-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ description DNS
+ destination {
+ address 172.31.0.254
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 11 {
+ action accept
+ description DHCP
+ destination {
+ port 67
+ }
+ protocol udp
+ }
+ rule 15 {
+ action accept
+ destination {
+ address 172.31.0.254
+ }
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 210 {
+ action accept
+ description "AUDIO-STREAM Broadcast"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name GUEST-WAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 25 {
+ action accept
+ description SMTP
+ destination {
+ port 25,587
+ }
+ protocol tcp
+ }
+ rule 53 {
+ action accept
+ destination {
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 60 {
+ action accept
+ source {
+ address 172.31.0.200
+ }
+ }
+ rule 80 {
+ action accept
+ source {
+ address 172.31.0.200
+ }
+ }
+ rule 100 {
+ action accept
+ protocol icmp
+ }
+ rule 110 {
+ action accept
+ description POP3
+ destination {
+ port 110,995
+ }
+ protocol tcp
+ }
+ rule 123 {
+ action accept
+ description "NTP Client"
+ destination {
+ port 123
+ }
+ protocol udp
+ }
+ rule 143 {
+ action accept
+ description IMAP
+ destination {
+ port 143,993
+ }
+ protocol tcp
+ }
+ rule 200 {
+ action accept
+ destination {
+ port 80,443
+ }
+ protocol tcp
+ }
+ rule 500 {
+ action accept
+ description "L2TP IPSec"
+ destination {
+ port 500,4500
+ }
+ protocol udp
+ }
+ rule 600 {
+ action accept
+ destination {
+ port 5222-5224
+ }
+ protocol tcp
+ }
+ rule 601 {
+ action accept
+ destination {
+ port 3478-3497,4500,16384-16387,16393-16402
+ }
+ protocol udp
+ }
+ rule 1000 {
+ action accept
+ source {
+ address 172.31.0.184
+ }
+ }
+ }
+ name IOT-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "MEDIA-STREAMING-CLIENTS Devices to IOT"
+ protocol tcp_udp
+ source {
+ group {
+ address-group MEDIA-STREAMING-CLIENTS
+ }
+ }
+ }
+ rule 110 {
+ action accept
+ description "AUDIO-STREAM Devices to IOT"
+ protocol tcp_udp
+ source {
+ group {
+ address-group AUDIO-STREAM
+ }
+ }
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name IOT-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "AppleTV to LAN"
+ destination {
+ group {
+ port-group SMART-TV-PORTS
+ }
+ }
+ protocol tcp_udp
+ source {
+ group {
+ address-group MEDIA-STREAMING-CLIENTS
+ }
+ }
+ }
+ rule 110 {
+ action accept
+ description "AUDIO-STREAM Devices to LAN"
+ protocol tcp_udp
+ source {
+ group {
+ address-group AUDIO-STREAM
+ }
+ }
+ }
+ }
+ name IOT-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ description DNS
+ destination {
+ address 172.16.254.30
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 11 {
+ action accept
+ description DHCP
+ destination {
+ port 67
+ }
+ protocol udp
+ }
+ rule 15 {
+ action accept
+ destination {
+ address 172.16.35.254
+ }
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 201 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 172.16.35.254
+ port 5353
+ }
+ protocol udp
+ }
+ rule 210 {
+ action accept
+ description "AUDIO-STREAM Broadcast"
+ destination {
+ port 1900,1902,6969
+ }
+ protocol udp
+ }
+ }
+ name IOT-WAN {
+ default-action accept
+ }
+ name LAN-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 22 {
+ action accept
+ description "SSH into DMZ"
+ destination {
+ port 22
+ }
+ protocol tcp
+ }
+ rule 100 {
+ action accept
+ destination {
+ group {
+ address-group DMZ-WEBSERVER
+ }
+ port 22,80,443
+ }
+ protocol tcp
+ }
+ }
+ name LAN-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name LAN-IOT {
+ default-action accept
+ }
+ name LAN-LOCAL {
+ default-action accept
+ }
+ name LAN-WAN {
+ default-action accept
+ }
+ name LOCAL-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name LOCAL-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 5 {
+ action accept
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name LOCAL-IOT {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 5 {
+ action accept
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900,6969
+ }
+ protocol udp
+ }
+ }
+ name LOCAL-LAN {
+ default-action accept
+ }
+ name LOCAL-WAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ protocol icmp
+ }
+ rule 50 {
+ action accept
+ description DNS
+ destination {
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 80 {
+ action accept
+ destination {
+ port 80,443
+ }
+ protocol tcp
+ }
+ rule 123 {
+ action accept
+ description NTP
+ destination {
+ port 123
+ }
+ protocol udp
+ }
+ }
+ name WAN-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ destination {
+ address 172.16.36.10
+ port 80,443
+ }
+ protocol tcp
+ }
+ }
+ name WAN-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 1000 {
+ action accept
+ destination {
+ address 172.31.0.184
+ }
+ }
+ rule 8000 {
+ action accept
+ destination {
+ address 172.31.0.200
+ port 10000
+ }
+ protocol udp
+ }
+ }
+ name WAN-IOT {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name WAN-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 1000 {
+ action accept
+ destination {
+ address 172.16.33.40
+ port 3389
+ }
+ protocol tcp
+ source {
+ group {
+ network-group SSH-IN-ALLOW
+ }
+ }
+ }
+ }
+ name WAN-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 22 {
+ action accept
+ destination {
+ port 22
+ }
+ protocol tcp
+ source {
+ group {
+ network-group SSH-IN-ALLOW
+ }
+ }
+ }
+ }
+ options {
+ interface pppoe0 {
+ adjust-mss 1452
+ adjust-mss6 1432
+ }
+ }
+ receive-redirects disable
+ send-redirects enable
+ source-validation disable
+ syn-cookies enable
+ twa-hazards-protection disable
+}
+interfaces {
+ dummy dum0 {
+ address 172.16.254.30/32
+ }
+ ethernet eth0 {
+ duplex auto
+ speed auto
+ vif 5 {
+ address 172.16.37.254/24
+ }
+ vif 10 {
+ address 172.16.33.254/24
+ }
+ vif 20 {
+ address 172.31.0.254/24
+ }
+ vif 35 {
+ address 172.16.35.254/24
+ }
+ vif 50 {
+ address 172.16.36.254/24
+ }
+ vif 100 {
+ address 172.16.100.254/24
+ }
+ vif 201 {
+ address 172.18.201.254/24
+ }
+ vif 202 {
+ address 172.18.202.254/24
+ }
+ vif 203 {
+ address 172.18.203.254/24
+ }
+ vif 204 {
+ address 172.18.204.254/24
+ }
+ }
+ ethernet eth1 {
+ vif 7 {
+ description FTTH-PPPoE
+ }
+ }
+ loopback lo {
+ address 172.16.254.30/32
+ }
+ pppoe pppoe0 {
+ authentication {
+ password vyos
+ user vyos
+ }
+ default-route auto
+ description "FTTH 100/50MBit"
+ dhcpv6-options {
+ pd 0 {
+ interface eth0.10 {
+ address 1
+ sla-id 10
+ }
+ interface eth0.20 {
+ address 1
+ sla-id 20
+ }
+ length 56
+ }
+ }
+ ipv6 {
+ address {
+ autoconf
+ }
+ }
+ mtu 1492
+ no-peer-dns
+ source-interface eth1.7
+ }
+}
+nat {
+ destination {
+ rule 100 {
+ description HTTP(S)
+ destination {
+ port 80,443
+ }
+ inbound-interface pppoe0
+ log
+ protocol tcp
+ translation {
+ address 172.16.36.10
+ }
+ }
+ rule 1000 {
+ destination {
+ port 3389
+ }
+ disable
+ inbound-interface pppoe0
+ protocol tcp
+ translation {
+ address 172.16.33.40
+ }
+ }
+ rule 8000 {
+ destination {
+ port 10000
+ }
+ inbound-interface pppoe0
+ log
+ protocol udp
+ translation {
+ address 172.31.0.200
+ }
+ }
+ }
+ source {
+ rule 100 {
+ log
+ outbound-interface pppoe0
+ source {
+ address 172.16.32.0/19
+ }
+ translation {
+ address masquerade
+ }
+ }
+ rule 200 {
+ outbound-interface pppoe0
+ source {
+ address 172.16.100.0/24
+ }
+ translation {
+ address masquerade
+ }
+ }
+ rule 300 {
+ outbound-interface pppoe0
+ source {
+ address 172.31.0.0/24
+ }
+ translation {
+ address masquerade
+ }
+ }
+ rule 400 {
+ outbound-interface pppoe0
+ source {
+ address 172.18.200.0/21
+ }
+ translation {
+ address masquerade
+ }
+ }
+ }
+}
+protocols {
+ static {
+ interface-route6 2000::/3 {
+ next-hop-interface pppoe0 {
+ }
+ }
+ route 10.0.0.0/8 {
+ blackhole {
+ distance 254
+ }
+ }
+ route 169.254.0.0/16 {
+ blackhole {
+ distance 254
+ }
+ }
+ route 172.16.0.0/12 {
+ blackhole {
+ distance 254
+ }
+ }
+ route 192.168.0.0/16 {
+ blackhole {
+ distance 254
+ }
+ }
+ }
+}
+service {
+ dhcp-server {
+ shared-network-name BACKBONE {
+ authoritative
+ subnet 172.16.37.0/24 {
+ default-router 172.16.37.254
+ dns-server 172.16.254.30
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ ntp-server 172.16.254.30
+ range 0 {
+ start 172.16.37.120
+ stop 172.16.37.149
+ }
+ static-mapping AP1.wue3 {
+ ip-address 172.16.37.231
+ mac-address 18:e8:29:6c:c3:a5
+ }
+ }
+ }
+ shared-network-name GUEST {
+ authoritative
+ subnet 172.31.0.0/24 {
+ default-router 172.31.0.254
+ dns-server 172.31.0.254
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ range 0 {
+ start 172.31.0.100
+ stop 172.31.0.199
+ }
+ static-mapping host01 {
+ ip-address 172.31.0.200
+ mac-address 00:50:00:00:00:01
+ }
+ static-mapping host02 {
+ ip-address 172.31.0.184
+ mac-address 00:50:00:00:00:02
+ }
+ }
+ }
+ shared-network-name IOT {
+ authoritative
+ subnet 172.16.35.0/24 {
+ default-router 172.16.35.254
+ dns-server 172.16.254.30
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ ntp-server 172.16.254.30
+ range 0 {
+ start 172.16.35.101
+ stop 172.16.35.149
+ }
+ }
+ }
+ shared-network-name LAN {
+ authoritative
+ subnet 172.16.33.0/24 {
+ default-router 172.16.33.254
+ dns-server 172.16.254.30
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ ntp-server 172.16.254.30
+ range 0 {
+ start 172.16.33.100
+ stop 172.16.33.189
+ }
+ }
+ }
+ }
+ dns {
+ forwarding {
+ allow-from 172.16.0.0/12
+ cache-size 0
+ domain 16.172.in-addr.arpa {
+ addnta
+ recursion-desired
+ server 172.16.100.10
+ server 172.16.100.20
+ server 172.16.110.30
+ }
+ domain 18.172.in-addr.arpa {
+ addnta
+ recursion-desired
+ server 172.16.100.10
+ server 172.16.100.20
+ server 172.16.110.30
+ }
+ domain vyos.net {
+ addnta
+ recursion-desired
+ server 172.16.100.20
+ server 172.16.100.10
+ server 172.16.110.30
+ }
+ ignore-hosts-file
+ listen-address 172.16.254.30
+ listen-address 172.31.0.254
+ negative-ttl 60
+ }
+ }
+ lldp {
+ legacy-protocols {
+ cdp
+ }
+ snmp {
+ enable
+ }
+ }
+ mdns {
+ repeater {
+ interface eth0.35
+ interface eth0.10
+ }
+ }
+ router-advert {
+ interface eth0.10 {
+ prefix ::/64 {
+ preferred-lifetime 2700
+ valid-lifetime 5400
+ }
+ }
+ interface eth0.20 {
+ prefix ::/64 {
+ preferred-lifetime 2700
+ valid-lifetime 5400
+ }
+ }
+ }
+ snmp {
+ community fooBar {
+ authorization ro
+ network 172.16.100.0/24
+ }
+ contact "VyOS maintainers and contributors <maintainers@vyos.io>"
+ listen-address 172.16.254.30 {
+ port 161
+ }
+ location "The Internet"
+ }
+ ssh {
+ disable-host-validation
+ port 22
+ }
+}
+system {
+ config-management {
+ commit-revisions 200
+ }
+ conntrack {
+ expect-table-size 2048
+ hash-size 32768
+ modules {
+ sip {
+ disable
+ }
+ }
+ table-size 262144
+ timeout {
+ icmp 30
+ other 600
+ udp {
+ other 300
+ stream 300
+ }
+ }
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos.net
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 172.16.254.30
+ ntp {
+ allow-clients {
+ address 172.16.0.0/12
+ }
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ option {
+ ctrl-alt-delete ignore
+ reboot-on-panic
+ startup-beep
+ }
+ syslog {
+ global {
+ facility all {
+ level debug
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ host 172.16.100.1 {
+ facility all {
+ level warning
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+traffic-policy {
+ shaper QoS {
+ bandwidth 50mbit
+ default {
+ bandwidth 100%
+ burst 15k
+ queue-limit 1000
+ queue-type fq-codel
+ }
+ }
+}
+zone-policy {
+ zone DMZ {
+ default-action drop
+ from GUEST {
+ firewall {
+ name GUEST-DMZ
+ }
+ }
+ from LAN {
+ firewall {
+ name LAN-DMZ
+ }
+ }
+ from LOCAL {
+ firewall {
+ name LOCAL-DMZ
+ }
+ }
+ from WAN {
+ firewall {
+ name WAN-DMZ
+ }
+ }
+ interface eth0.50
+ }
+ zone GUEST {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-GUEST
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-GUEST
+ }
+ }
+ from LAN {
+ firewall {
+ name LAN-GUEST
+ }
+ }
+ from LOCAL {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LOCAL-GUEST
+ }
+ }
+ from WAN {
+ firewall {
+ ipv6-name ALLOW-ESTABLISHED-6
+ name WAN-GUEST
+ }
+ }
+ interface eth0.20
+ }
+ zone IOT {
+ default-action drop
+ from GUEST {
+ firewall {
+ name GUEST-IOT
+ }
+ }
+ from LAN {
+ firewall {
+ name LAN-IOT
+ }
+ }
+ from LOCAL {
+ firewall {
+ name LOCAL-IOT
+ }
+ }
+ from WAN {
+ firewall {
+ name WAN-IOT
+ }
+ }
+ interface eth0.35
+ }
+ zone LAN {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-LAN
+ }
+ }
+ from GUEST {
+ firewall {
+ name GUEST-LAN
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-LAN
+ }
+ }
+ from LOCAL {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LOCAL-LAN
+ }
+ }
+ from WAN {
+ firewall {
+ ipv6-name ALLOW-ESTABLISHED-6
+ name WAN-LAN
+ }
+ }
+ interface eth0.5
+ interface eth0.10
+ interface eth0.100
+ interface eth0.201
+ interface eth0.202
+ interface eth0.203
+ interface eth0.204
+ }
+ zone LOCAL {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-LOCAL
+ }
+ }
+ from GUEST {
+ firewall {
+ ipv6-name ALLOW-ESTABLISHED-6
+ name GUEST-LOCAL
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-LOCAL
+ }
+ }
+ from LAN {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LAN-LOCAL
+ }
+ }
+ from WAN {
+ firewall {
+ ipv6-name WAN-LOCAL-6
+ name WAN-LOCAL
+ }
+ }
+ local-zone
+ }
+ zone WAN {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-WAN
+ }
+ }
+ from GUEST {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name GUEST-WAN
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-WAN
+ }
+ }
+ from LAN {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LAN-WAN
+ }
+ }
+ from LOCAL {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LOCAL-WAN
+ }
+ }
+ interface pppoe0
+ }
+}
+
+
+// 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@18: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@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3-beta-202101091250
diff --git a/smoketest/configs/dialup-router-medium-vpn b/smoketest/configs/dialup-router-medium-vpn
index dfb3d9621..af7c075e4 100644
--- a/smoketest/configs/dialup-router-medium-vpn
+++ b/smoketest/configs/dialup-router-medium-vpn
@@ -624,6 +624,7 @@ system {
}
}
name-server 192.168.0.1
+ name-servers-dhcp pppoe0
ntp {
allow-clients {
address 192.168.0.0/16
diff --git a/smoketest/configs/isis-small b/smoketest/configs/isis-small
new file mode 100644
index 000000000..247ae32b5
--- /dev/null
+++ b/smoketest/configs/isis-small
@@ -0,0 +1,104 @@
+interfaces {
+ dummy dum0 {
+ address 203.0.113.1/24
+ }
+ ethernet eth0 {
+ duplex auto
+ speed auto
+ }
+ ethernet eth1 {
+ address 192.0.2.1/24
+ duplex auto
+ speed auto
+ }
+ ethernet eth2 {
+ duplex auto
+ speed auto
+ }
+ ethernet eth3 {
+ duplex auto
+ speed auto
+ }
+}
+policy {
+ prefix-list EXPORT-ISIS {
+ rule 10 {
+ action permit
+ prefix 203.0.113.0/24
+ }
+ }
+ route-map EXPORT-ISIS {
+ rule 10 {
+ action permit
+ match {
+ ip {
+ address {
+ prefix-list EXPORT-ISIS
+ }
+ }
+ }
+ }
+ }
+}
+protocols {
+ isis FOO {
+ interface eth1 {
+ bfd
+ }
+ net 49.0001.1921.6800.1002.00
+ redistribute {
+ ipv4 {
+ connected {
+ level-2 {
+ route-map EXPORT-ISIS
+ }
+ }
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 200
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos.io
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ level admin
+ }
+ }
+ 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
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+
+
+// 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@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@7:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3.0-rc1
diff --git a/smoketest/configs/tunnel-broker b/smoketest/configs/tunnel-broker
new file mode 100644
index 000000000..9a1e79719
--- /dev/null
+++ b/smoketest/configs/tunnel-broker
@@ -0,0 +1,135 @@
+interfaces {
+ dummy dum0 {
+ address 192.0.2.0/32
+ }
+ dummy dum1 {
+ address 192.0.2.1/32
+ }
+ dummy dum2 {
+ address 192.0.2.2/32
+ }
+ dummy dum3 {
+ address 192.0.2.3/32
+ }
+ dummy dum4 {
+ address 192.0.2.4/32
+ }
+ ethernet eth0 {
+ duplex auto
+ smp-affinity auto
+ speed auto
+ address 172.18.202.10/24
+ }
+ l2tpv3 l2tpeth10 {
+ destination-port 5010
+ encapsulation ip
+ local-ip 172.18.202.10
+ peer-session-id 110
+ peer-tunnel-id 10
+ remote-ip 172.18.202.110
+ session-id 110
+ source-port 5010
+ tunnel-id 10
+ }
+ l2tpv3 l2tpeth20 {
+ destination-port 5020
+ encapsulation ip
+ local-ip 172.18.202.10
+ peer-session-id 120
+ peer-tunnel-id 20
+ remote-ip 172.18.202.120
+ session-id 120
+ source-port 5020
+ tunnel-id 20
+ }
+ l2tpv3 l2tpeth30 {
+ destination-port 5030
+ encapsulation ip
+ local-ip 172.18.202.10
+ peer-session-id 130
+ peer-tunnel-id 30
+ remote-ip 172.18.202.130
+ session-id 130
+ source-port 5030
+ tunnel-id 30
+ }
+ tunnel tun100 {
+ address 172.16.0.1/30
+ encapsulation gre-bridge
+ local-ip 192.0.2.1
+ remote-ip 192.0.2.100
+ }
+ tunnel tun200 {
+ address 172.16.0.5/30
+ encapsulation gre
+ dhcp-interface eth0
+ remote-ip 192.0.2.101
+ }
+ tunnel tun300 {
+ address 172.16.0.9/30
+ encapsulation ipip
+ local-ip 192.0.2.2
+ remote-ip 192.0.2.102
+ }
+ tunnel tun400 {
+ address 172.16.0.13/30
+ encapsulation gre-bridge
+ local-ip 192.0.2.3
+ remote-ip 192.0.2.103
+ }
+ tunnel tun500 {
+ address 172.16.0.17/30
+ encapsulation gre
+ local-ip 192.0.2.4
+ remote-ip 192.0.2.104
+ }
+}
+protocols {
+ static {
+ route 0.0.0.0/0 {
+ next-hop 172.18.202.254 {
+ }
+ }
+ }
+}
+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. */
+/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */
+/* Release version: 1.2.6-S1 */
diff --git a/smoketest/configs/vrf-basic b/smoketest/configs/vrf-basic
new file mode 100644
index 000000000..ded33f683
--- /dev/null
+++ b/smoketest/configs/vrf-basic
@@ -0,0 +1,231 @@
+interfaces {
+ ethernet eth0 {
+ address 192.0.2.1/24
+ }
+ ethernet eth1 {
+ duplex auto
+ speed auto
+ vrf green
+ }
+ ethernet eth2 {
+ vrf red
+ }
+}
+protocols {
+ static {
+ route 0.0.0.0/0 {
+ next-hop 192.0.2.254 {
+ distance 10
+ }
+ }
+ table 10 {
+ interface-route 1.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 20
+ }
+ }
+ interface-route 2.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 20
+ }
+ }
+ interface-route 3.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 20
+ }
+ }
+ }
+ table 20 {
+ interface-route 4.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 20
+ }
+ }
+ interface-route 5.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 50
+ }
+ }
+ interface-route 6.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 60
+ }
+ }
+ interface-route6 2001:db8:100::/40 {
+ next-hop-interface eth1 {
+ distance 20
+ }
+ }
+ interface-route6 2001:db8::/40 {
+ next-hop-interface eth1 {
+ distance 10
+ }
+ }
+ route 11.0.0.0/8 {
+ next-hop 1.1.1.1 {
+ next-hop-interface eth0
+ }
+ }
+ route 12.0.0.0/8 {
+ next-hop 1.1.1.1 {
+ next-hop-interface eth0
+ }
+ }
+ route 13.0.0.0/8 {
+ next-hop 1.1.1.1 {
+ next-hop-interface eth0
+ }
+ }
+ }
+ table 30 {
+ interface-route6 2001:db8:200::/40 {
+ next-hop-interface eth1 {
+ distance 20
+ }
+ }
+ route 14.0.0.0/8 {
+ next-hop 2.2.1.1 {
+ next-hop-interface eth1
+ }
+ }
+ route 15.0.0.0/8 {
+ next-hop 2.2.1.1 {
+ next-hop-interface eth1
+ }
+ }
+ }
+ }
+ vrf green {
+ static {
+ interface-route 100.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 200
+ next-hop-vrf default
+ }
+ }
+ interface-route 101.0.0.0/8 {
+ next-hop-interface eth0 {
+ next-hop-vrf default
+ }
+ next-hop-interface eth1 {
+ }
+ }
+ interface-route6 2001:db8:300::/40 {
+ next-hop-interface eth1 {
+ distance 20
+ next-hop-vrf default
+ }
+ }
+ route 20.0.0.0/8 {
+ next-hop 1.1.1.1 {
+ next-hop-interface eth1
+ next-hop-vrf default
+ }
+ }
+ route 21.0.0.0/8 {
+ next-hop 2.2.1.1 {
+ next-hop-interface eth1
+ next-hop-vrf default
+ }
+ }
+ route6 2001:db8:100::/40 {
+ next-hop fe80::1 {
+ interface eth0
+ next-hop-vrf default
+ }
+ }
+ }
+ }
+ vrf red {
+ static {
+ interface-route 103.0.0.0/8 {
+ next-hop-interface eth0 {
+ distance 201
+ next-hop-vrf default
+ }
+ }
+ interface-route 104.0.0.0/8 {
+ next-hop-interface eth0 {
+ next-hop-vrf default
+ }
+ next-hop-interface eth1 {
+ next-hop-vrf default
+ }
+ }
+ interface-route6 2001:db8:400::/40 {
+ next-hop-interface eth1 {
+ distance 24
+ next-hop-vrf default
+ }
+ }
+ route 30.0.0.0/8 {
+ next-hop 1.1.1.1 {
+ next-hop-interface eth1
+ }
+ }
+ route 40.0.0.0/8 {
+ next-hop 2.2.1.1 {
+ next-hop-interface eth1
+ next-hop-vrf default
+ }
+ }
+ route6 2001:db8:100::/40 {
+ next-hop fe80::1 {
+ interface eth0
+ next-hop-vrf default
+ }
+ }
+ }
+ }
+}
+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 ""
+ }
+ }
+ }
+ nt
+ 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
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+vrf {
+ name green {
+ table 1000
+ }
+ name red {
+ table 2000
+ }
+}
+
+// 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@18: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@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3-beta-202101231023
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 6f8eda26a..60cad32bb 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -214,7 +214,7 @@ class BasicInterfaceTest:
self.cli_commit()
for interface in self._interfaces:
- self.assertTrue(AF_INET6 not in ifaddresses(interface))
+ self.assertNotIn(AF_INET6, ifaddresses(interface))
def test_interface_mtu(self):
if not self._test_mtu:
@@ -246,11 +246,19 @@ class BasicInterfaceTest:
for intf in self._interfaces:
base = self._base_path + [intf]
self.cli_set(base + ['mtu', self._mtu])
- self.cli_set(base + ['ipv6', 'address', 'no-default-link-local'])
for option in self._options.get(intf, []):
self.cli_set(base + option.split())
+ # check validate() - can not set low MTU if 'no-default-link-local'
+ # is not set on CLI
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ for intf in self._interfaces:
+ base = self._base_path + [intf]
+ self.cli_set(base + ['ipv6', 'address', 'no-default-link-local'])
+
# commit interface changes
self.cli_commit()
@@ -411,6 +419,16 @@ class BasicInterfaceTest:
tmp = read_file(f'/sys/class/net/{vif}/mtu')
self.assertEqual(tmp, self._mtu)
+ # T3972: remove vif-c interfaces from vif-s
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vif_s in self._qinq_range:
+ base = self._base_path + [interface, 'vif-s', vif_s, 'vif-c']
+ self.cli_delete(base)
+
+ self.cli_commit()
+
+
def test_interface_ip_options(self):
if not self._test_ip:
self.skipTest('not supported')
@@ -438,28 +456,30 @@ class BasicInterfaceTest:
tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms')
self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_filter')
+ proc_base = f'/proc/sys/net/ipv4/conf/{interface}'
+
+ tmp = read_file(f'{proc_base}/arp_filter')
self.assertEqual('0', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_accept')
+ tmp = read_file(f'{proc_base}/arp_accept')
self.assertEqual('1', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_announce')
+ tmp = read_file(f'{proc_base}/arp_announce')
self.assertEqual('1', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_ignore')
+ tmp = read_file(f'{proc_base}/arp_ignore')
self.assertEqual('1', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/forwarding')
+ tmp = read_file(f'{proc_base}/forwarding')
self.assertEqual('0', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp')
+ tmp = read_file(f'{proc_base}/proxy_arp')
self.assertEqual('1', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp_pvlan')
+ tmp = read_file(f'{proc_base}/proxy_arp_pvlan')
self.assertEqual('1', tmp)
- tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/rp_filter')
+ tmp = read_file(f'{proc_base}/rp_filter')
self.assertEqual('2', tmp)
def test_interface_ipv6_options(self):
@@ -479,10 +499,12 @@ class BasicInterfaceTest:
self.cli_commit()
for interface in self._interfaces:
- tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding')
+ proc_base = f'/proc/sys/net/ipv6/conf/{interface}'
+
+ tmp = read_file(f'{proc_base}/forwarding')
self.assertEqual('0', tmp)
- tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits')
+ tmp = read_file(f'{proc_base}/dad_transmits')
self.assertEqual(dad_transmits, tmp)
def test_dhcpv6_client_options(self):
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index 18e4e567e..93b2ca150 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -20,7 +20,9 @@ from time import sleep
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos import ConfigError
+from vyos.defaults import commit_lock
from vyos.util import cmd
+from vyos.util import run
save_config = '/tmp/vyos-smoketest-save'
@@ -70,21 +72,16 @@ class VyOSUnitTestSHIM:
def cli_commit(self):
self._session.commit()
+ # during a commit there is a process opening commit_lock, and run() returns 0
+ while run(f'sudo lsof | grep -q {commit_lock}') == 0:
+ sleep(0.250)
def getFRRconfig(self, string, end='$'):
""" Retrieve current "running configuration" from FRR """
command = f'vtysh -c "show run" | sed -n "/^{string}{end}/,/^!/p"'
-
- count = 0
- tmp = ''
- while count < 10 and tmp == '':
- # Let FRR settle after a config change first before harassing it again
- sleep(1)
- tmp = cmd(command)
- count += 1
-
- if self.debug or tmp == '':
+ out = cmd(command)
+ if self.debug:
import pprint
print(f'\n\ncommand "{command}" returned:\n')
- pprint.pprint(tmp)
- return tmp
+ pprint.pprint(out)
+ return out
diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py
new file mode 100755
index 000000000..751fd105e
--- /dev/null
+++ b/smoketest/scripts/cli/test_ha_vrrp.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig.vrrp import VRRP
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.template import inc_ip
+
+PROCESS_NAME = 'keepalived'
+KEEPALIVED_CONF = VRRP.location['config']
+base_path = ['high-availability', 'vrrp']
+
+vrrp_interface = 'eth1'
+groups = ['VLAN77', 'VLAN78', 'VLAN201']
+
+def getConfig(string, end='}'):
+ command = f'cat {KEEPALIVED_CONF} | sed -n "/^{string}/,/^{end}/p"'
+ out = cmd(command)
+ return out
+
+class TestVRRP(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id])
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Process must be terminated after deleting the config
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_default_values(self):
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ vip = f'100.64.{vlan_id}.1/24'
+ group_base = base_path + ['group', group]
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
+
+ self.cli_set(group_base + ['description', group])
+ self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}'])
+ self.cli_set(group_base + ['virtual-address', vip])
+ self.cli_set(group_base + ['vrid', vlan_id])
+
+ # commit changes
+ self.cli_commit()
+
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ vip = f'100.64.{vlan_id}.1/24'
+
+ config = getConfig(f'vrrp_instance {group}')
+
+ self.assertIn(f'# {group}', config)
+ self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config)
+ self.assertIn(f'virtual_router_id {vlan_id}', config)
+ self.assertIn(f'priority 100', config) # default value
+ self.assertIn(f'advert_int 1', config) # default value
+ self.assertIn(f'preempt_delay 0', config) # default value
+ self.assertNotIn(f'use_vmac', config)
+ self.assertIn(f' {vip}', config)
+
+ def test_02_simple_options(self):
+ advertise_interval = '77'
+ priority = '123'
+ preempt_delay = '400'
+
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ vip = f'100.64.{vlan_id}.1/24'
+ group_base = base_path + ['group', group]
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
+
+ self.cli_set(group_base + ['description', group])
+ self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}'])
+ self.cli_set(group_base + ['virtual-address', vip])
+ self.cli_set(group_base + ['vrid', vlan_id])
+
+ self.cli_set(group_base + ['advertise-interval', advertise_interval])
+ self.cli_set(group_base + ['priority', priority])
+ self.cli_set(group_base + ['preempt-delay', preempt_delay])
+
+ self.cli_set(group_base + ['rfc3768-compatibility'])
+
+ # Authentication
+ self.cli_set(group_base + ['authentication', 'type', 'plaintext-password'])
+ self.cli_set(group_base + ['authentication', 'password', f'vyos-{group}'])
+
+ # commit changes
+ self.cli_commit()
+
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ vip = f'100.64.{vlan_id}.1/24'
+
+ config = getConfig(f'vrrp_instance {group}')
+ self.assertIn(f'# {group}', config)
+ self.assertIn(f'state BACKUP', config)
+ self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config)
+ self.assertIn(f'virtual_router_id {vlan_id}', config)
+ self.assertIn(f'priority {priority}', config)
+ self.assertIn(f'advert_int {advertise_interval}', config)
+ self.assertIn(f'preempt_delay {preempt_delay}', config)
+ self.assertIn(f'use_vmac {vrrp_interface}.{vlan_id}v{vlan_id}', config)
+ self.assertIn(f' {vip}', config)
+
+ # Authentication
+ self.assertIn(f'auth_pass "vyos-{group}"', config)
+ self.assertIn(f'auth_type PASS', config)
+
+ def test_03_sync_group(self):
+ sync_group = 'VyOS'
+
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ vip = f'100.64.{vlan_id}.1/24'
+ group_base = base_path + ['group', group]
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
+
+ self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}'])
+ self.cli_set(group_base + ['virtual-address', vip])
+ self.cli_set(group_base + ['vrid', vlan_id])
+
+ self.cli_set(base_path + ['sync-group', sync_group, 'member', group])
+
+ # commit changes
+ self.cli_commit()
+
+ for group in groups:
+ vlan_id = group.lstrip('VLAN')
+ vip = f'100.64.{vlan_id}.1/24'
+ config = getConfig(f'vrrp_instance {group}')
+
+ self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config)
+ self.assertIn(f'virtual_router_id {vlan_id}', config)
+ self.assertNotIn(f'use_vmac', config)
+ self.assertIn(f' {vip}', config)
+
+ config = getConfig(f'vrrp_sync_group {sync_group}')
+ self.assertIn(r'group {', config)
+ for group in groups:
+ self.assertIn(f'{group}', config)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index 4014c1a4c..2152dba72 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -63,6 +63,32 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
super().tearDown()
+ def test_isolated_interfaces(self):
+ # Add member interfaces to bridge and set STP cost/priority
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ self.cli_set(base + ['stp'])
+
+ # assign members to bridge interface
+ for member in self._members:
+ base_member = base + ['member', 'interface', member]
+ self.cli_set(base_member + ['isolated'])
+
+ # commit config
+ self.cli_commit()
+
+ for interface in self._interfaces:
+ tmp = get_interface_config(interface)
+ # STP must be enabled as configured above
+ self.assertEqual(1, tmp['linkinfo']['info_data']['stp_state'])
+
+ # validate member interface configuration
+ for member in self._members:
+ tmp = get_interface_config(member)
+ # Isolated must be enabled as configured above
+ self.assertTrue(tmp['linkinfo']['info_slave_data']['isolated'])
+
+
def test_add_remove_bridge_member(self):
# Add member interfaces to bridge and set STP cost/priority
for interface in self._interfaces:
@@ -97,12 +123,34 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
cost += 1
priority += 1
+
+ def test_vif_8021q_interfaces(self):
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ self.cli_set(base + ['enable-vlan'])
+ super().test_vif_8021q_interfaces()
+
+ def test_vif_8021q_lower_up_down(self):
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ self.cli_set(base + ['enable-vlan'])
+ super().test_vif_8021q_lower_up_down()
+
+ def test_vif_8021q_mtu_limits(self):
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ self.cli_set(base + ['enable-vlan'])
+ super().test_vif_8021q_mtu_limits()
+
def test_bridge_vlan_filter(self):
+ vif_vlan = 2
# Add member interface to bridge and set VLAN filter
for interface in self._interfaces:
base = self._base_path + [interface]
- self.cli_set(base + ['vif', '1', 'address', '192.0.2.1/24'])
- self.cli_set(base + ['vif', '2', 'address', '192.0.3.1/24'])
+ self.cli_set(base + ['enable-vlan'])
+ self.cli_set(base + ['address', '192.0.2.1/24'])
+ self.cli_set(base + ['vif', str(vif_vlan), 'address', '192.0.3.1/24'])
+ self.cli_set(base + ['vif', str(vif_vlan), 'mtu', self._mtu])
vlan_id = 101
allowed_vlan = 2
@@ -174,6 +222,7 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
for interface in self._interfaces:
self.cli_delete(self._base_path + [interface, 'member'])
+
def test_bridge_vlan_members(self):
# T2945: ensure that VIFs are not dropped from bridge
vifs = ['300', '400']
diff --git a/smoketest/scripts/cli/test_interfaces_geneve.py b/smoketest/scripts/cli/test_interfaces_geneve.py
index 8a18d8344..692206f63 100755
--- a/smoketest/scripts/cli/test_interfaces_geneve.py
+++ b/smoketest/scripts/cli/test_interfaces_geneve.py
@@ -16,7 +16,6 @@
import unittest
-from vyos.configsession import ConfigSession
from base_interfaces_test import BasicInterfaceTest
class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index 1a52a0a5b..24df0af4d 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -23,7 +23,6 @@ from netifaces import interfaces
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py
index 3412ebae0..402fb4af5 100755
--- a/smoketest/scripts/cli/test_interfaces_pppoe.py
+++ b/smoketest/scripts/cli/test_interfaces_pppoe.py
@@ -20,7 +20,6 @@ import unittest
from psutil import process_iter
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import read_file
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index 3aed498b4..ff8778828 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -156,26 +156,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_delete(self._base_path + [interface])
self.cli_commit()
- def test_tunnel_verify_local_dhcp(self):
- # We can not use source-address and dhcp-interface at the same time
-
- interface = f'tun1020'
- local_if_addr = f'10.0.0.1/24'
-
- self.cli_set(self._base_path + [interface, 'address', local_if_addr])
- self.cli_set(self._base_path + [interface, 'encapsulation', 'gre'])
- self.cli_set(self._base_path + [interface, 'source-address', self.local_v4])
- self.cli_set(self._base_path + [interface, 'remote', remote_ip4])
- self.cli_set(self._base_path + [interface, 'dhcp-interface', 'eth0'])
-
- # source-address and dhcp-interface can not be used at the same time
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_delete(self._base_path + [interface, 'dhcp-interface'])
-
- # Check if commit is ok
- self.cli_commit()
-
def test_tunnel_parameters_gre(self):
interface = f'tun1030'
gre_key = '10'
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index 0fba0f460..184b411d7 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -16,7 +16,6 @@
import unittest
-from vyos.configsession import ConfigSession
from vyos.ifconfig import Interface
from vyos.util import get_interface_config
diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py
index d31ec0332..5562a697d 100755
--- a/smoketest/scripts/cli/test_interfaces_wireguard.py
+++ b/smoketest/scripts/cli/test_interfaces_wireguard.py
@@ -18,7 +18,6 @@ import os
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 0706f234e..75c628244 100755..100644
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -20,7 +20,6 @@ import json
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import dict_search
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index cdd2ad820..f1d195381 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
diff --git a/smoketest/scripts/cli/test_policy_local-route.py b/smoketest/scripts/cli/test_policy_local-route.py
index c742a930b..627e3da02 100755
--- a/smoketest/scripts/cli/test_policy_local-route.py
+++ b/smoketest/scripts/cli/test_policy_local-route.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index 0c4ed86d7..46a2bdcfa 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index b261e4164..073064939 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_igmp-proxy.py b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
index 1eaf21722..079b5bee5 100755
--- a/smoketest/scripts/cli/test_protocols_igmp-proxy.py
+++ b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import read_file
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
new file mode 100755
index 000000000..8abdd6d37
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.util import process_named_running
+
+PROCESS_NAME = 'isisd'
+base_path = ['protocols', 'isis']
+
+domain = 'VyOS'
+net = '49.0001.1921.6800.1002.00'
+
+class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls._interfaces = Section.interfaces('ethernet')
+
+ # call base-classes classmethod
+ super(cls, cls).setUpClass()
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def isis_base_config(self):
+ self.cli_set(base_path + ['net', net])
+ for interface in self._interfaces:
+ self.cli_set(base_path + ['interface', interface])
+
+ def test_isis_01_redistribute(self):
+ prefix_list = 'EXPORT-ISIS'
+ route_map = 'EXPORT-ISIS'
+ rule = '10'
+
+ self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'action', 'permit'])
+ self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'prefix', '203.0.113.0/24'])
+ self.cli_set(['policy', 'route-map', route_map, 'rule', rule, 'action', 'permit'])
+ self.cli_set(['policy', 'route-map', route_map, 'rule', rule, 'match', 'ip', 'address', 'prefix-list', prefix_list])
+
+ self.cli_set(base_path)
+
+ # verify() - net id and interface are mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.isis_base_config()
+ self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map])
+ self.cli_set(base_path + ['log-adjacency-changes'])
+
+ # Commit all changes
+ self.cli_commit()
+
+ # Verify all changes
+ tmp = self.getFRRconfig(f'router isis {domain}')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' log-adjacency-changes', tmp)
+ self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', tmp)
+
+ for interface in self._interfaces:
+ tmp = self.getFRRconfig(f'interface {interface}')
+ self.assertIn(f' ip router isis {domain}', tmp)
+ self.assertIn(f' ipv6 router isis {domain}', tmp)
+
+ self.cli_delete(['policy', 'route-map', route_map])
+ self.cli_delete(['policy', 'prefix-list', prefix_list])
+
+ def test_isis_02_zebra_route_map(self):
+ # Implemented because of T3328
+ route_map = 'foo-isis-in'
+
+ self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
+
+ self.isis_base_config()
+ self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map])
+ self.cli_set(base_path + ['route-map', route_map])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR configuration
+ zebra_route_map = f'ip protocol isis route-map {route_map}'
+ frrconfig = self.getFRRconfig(zebra_route_map)
+ self.assertIn(zebra_route_map, frrconfig)
+
+ # Remove the route-map again
+ self.cli_delete(base_path + ['route-map'])
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR configuration
+ frrconfig = self.getFRRconfig(zebra_route_map)
+ self.assertNotIn(zebra_route_map, frrconfig)
+
+ self.cli_delete(['policy', 'route-map', route_map])
+
+ def test_isis_03_default_information(self):
+ metric = '50'
+ route_map = 'default-foo-'
+
+ self.isis_base_config()
+ for afi in ['ipv4', 'ipv6']:
+ for level in ['level-1', 'level-2']:
+ self.cli_set(base_path + ['default-information', 'originate', afi, level, 'always'])
+ self.cli_set(base_path + ['default-information', 'originate', afi, level, 'metric', metric])
+ self.cli_set(base_path + ['default-information', 'originate', afi, level, 'route-map', route_map + level + afi])
+
+ # Commit all changes
+ self.cli_commit()
+
+ # Verify all changes
+ tmp = self.getFRRconfig(f'router isis {domain}')
+ self.assertIn(f' net {net}', tmp)
+
+ for afi in ['ipv4', 'ipv6']:
+ for level in ['level-1', 'level-2']:
+ route_map_name = route_map + level + afi
+ self.assertIn(f' default-information originate {afi} {level} always route-map {route_map_name} metric {metric}', tmp)
+
+ def test_isis_04_password(self):
+ password = 'foo'
+
+ self.isis_base_config()
+
+ self.cli_set(base_path + ['area-password', 'plaintext-password', password])
+ self.cli_set(base_path + ['area-password', 'md5', password])
+ self.cli_set(base_path + ['domain-password', 'plaintext-password', password])
+ self.cli_set(base_path + ['domain-password', 'md5', password])
+
+ # verify() - can not use both md5 and plaintext-password for area-password
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['area-password', 'md5', password])
+
+ # verify() - can not use both md5 and plaintext-password for domain-password
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['domain-password', 'md5', password])
+
+ # Commit all changes
+ self.cli_commit()
+
+ # Verify all changes
+ tmp = self.getFRRconfig(f'router isis {domain}')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' domain-password clear {password}', tmp)
+ self.assertIn(f' area-password clear {password}', tmp)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index c4eb3fdd8..7a11bcd2a 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.ifconfig import Section
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py
index 6f2028f2b..020b981fe 100755
--- a/smoketest/scripts/cli/test_protocols_rip.py
+++ b/smoketest/scripts/cli/test_protocols_rip.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.ifconfig import Section
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index 3380dc78b..b360c31d7 100755
--- a/smoketest/scripts/cli/test_protocols_ripng.py
+++ b/smoketest/scripts/cli/test_protocols_ripng.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.ifconfig import Section
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index 8212e9469..924d04bb4 100755
--- a/smoketest/scripts/cli/test_protocols_rpki.py
+++ b/smoketest/scripts/cli/test_protocols_rpki.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index de9b48de4..d1f8004b9 100755
--- a/smoketest/scripts/cli/test_protocols_static.py
+++ b/smoketest/scripts/cli/test_protocols_static.py
@@ -23,7 +23,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from netifaces import interfaces
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
diff --git a/smoketest/scripts/cli/test_service_bcast-relay.py b/smoketest/scripts/cli/test_service_bcast-relay.py
index 58b730ab4..87901869e 100755
--- a/smoketest/scripts/cli/test_service_bcast-relay.py
+++ b/smoketest/scripts/cli/test_service_bcast-relay.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from psutil import process_iter
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
base_path = ['service', 'broadcast-relay']
diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py
index db2edba54..bbfd9e032 100755
--- a/smoketest/scripts/cli/test_service_dhcp-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcp-relay.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index 815bd333a..14666db15 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import process_named_running
from vyos.util import read_file
@@ -37,12 +36,19 @@ dns_2 = inc_ip(subnet, 3)
domain_name = 'vyos.net'
class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
cidr_mask = subnet.split('/')[-1]
- self.cli_set(['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}'])
+ cls.cli_set(cls, ['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', 'dum8765'])
+ super(cls, cls).tearDownClass()
def tearDown(self):
- self.cli_delete(['interfaces', 'dummy', 'dum8765'])
self.cli_delete(base_path)
self.cli_commit()
@@ -59,9 +65,10 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['dns-server', dns_1])
- self.cli_set(pool + ['dns-server', dns_2])
+ self.cli_set(pool + ['name-server', dns_1])
+ self.cli_set(pool + ['name-server', dns_2])
self.cli_set(pool + ['domain-name', domain_name])
+ self.cli_set(pool + ['ping-check'])
# check validate() - No DHCP address range or active static-mapping set
with self.assertRaises(ConfigSessionError):
@@ -84,6 +91,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'option domain-name "{domain_name}";', config)
self.assertIn(f'default-lease-time 86400;', config)
self.assertIn(f'max-lease-time 86400;', config)
+ self.assertIn(f'ping-check true;', config)
self.assertIn(f'range {range_0_start} {range_0_stop};', config)
self.assertIn(f'range {range_1_start} {range_1_stop};', config)
self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
@@ -108,8 +116,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['dns-server', dns_1])
- self.cli_set(pool + ['dns-server', dns_2])
+ self.cli_set(pool + ['name-server', dns_1])
+ self.cli_set(pool + ['name-server', dns_2])
self.cli_set(pool + ['domain-name', domain_name])
self.cli_set(pool + ['ip-forwarding'])
self.cli_set(pool + ['smtp-server', smtp_server])
@@ -123,8 +131,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(pool + ['wpad-url', wpad])
self.cli_set(pool + ['server-identifier', server_identifier])
- self.cli_set(pool + ['static-route', 'destination-subnet', '10.0.0.0/24'])
- self.cli_set(pool + ['static-route', 'router', '192.0.2.1'])
+ self.cli_set(pool + ['static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1'])
# check validate() - No DHCP address range or active static-mapping set
with self.assertRaises(ConfigSessionError):
@@ -132,34 +139,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(pool + ['range', '0', 'start', range_0_start])
self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
- # failover
- failover_local = router
- failover_remote = inc_ip(router, 1)
-
- self.cli_set(pool + ['failover', 'local-address', failover_local])
- self.cli_set(pool + ['failover', 'name', shared_net_name])
- self.cli_set(pool + ['failover', 'peer-address', failover_remote])
- self.cli_set(pool + ['failover', 'status', 'primary'])
-
# commit changes
self.cli_commit()
config = read_file(DHCPD_CONF)
- self.assertIn(f'failover peer "{shared_net_name}"' + r' {', config)
- self.assertIn(f'primary;', config)
- self.assertIn(f'mclt 1800;', config)
- self.assertIn(f'mclt 1800;', config)
- self.assertIn(f'split 128;', config)
- self.assertIn(f'port 520;', config)
- self.assertIn(f'peer port 520;', config)
- self.assertIn(f'max-response-delay 30;', config)
- self.assertIn(f'max-unacked-updates 10;', config)
- self.assertIn(f'load balance max seconds 3;', config)
- self.assertIn(f'peer port 520;', config)
- self.assertIn(f'address {failover_local};', config)
- self.assertIn(f'peer address {failover_remote};', config)
-
network = address_from_cidr(subnet)
netmask = netmask_from_cidr(subnet)
self.assertIn(f'ddns-update-style none;', config)
@@ -185,8 +169,6 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'max-lease-time 86400;', config)
self.assertIn(f'range {range_0_start} {range_0_stop};', config)
self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
- self.assertIn(f'failover peer "{shared_net_name}";', config)
- self.assertIn(f'deny dynamic bootp clients;', config)
# weird syntax for those static routes
self.assertIn(f'option rfc3442-static-route 24,10,0,0,192,0,2,1, 0,192,0,2,1;', config)
@@ -202,8 +184,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['dns-server', dns_1])
- self.cli_set(pool + ['dns-server', dns_2])
+ self.cli_set(pool + ['name-server', dns_1])
+ self.cli_set(pool + ['name-server', dns_2])
self.cli_set(pool + ['domain-name', domain_name])
# check validate() - No DHCP address range or active static-mapping set
@@ -262,7 +244,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['dns-server', dns_1])
+ self.cli_set(pool + ['name-server', dns_1])
self.cli_set(pool + ['domain-name', domain_name])
self.cli_set(pool + ['lease', lease_time])
@@ -438,5 +420,68 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
+ def test_dhcp_failover(self):
+ shared_net_name = 'FAILOVER'
+ failover_name = 'VyOS-Failover'
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ # we use the first subnet IP address as default gateway
+ self.cli_set(pool + ['default-router', router])
+
+ # check validate() - No DHCP address range or active static-mapping set
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(pool + ['range', '0', 'start', range_0_start])
+ self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
+
+ # failover
+ failover_local = router
+ failover_remote = inc_ip(router, 1)
+
+ self.cli_set(base_path + ['failover', 'source-address', failover_local])
+ self.cli_set(base_path + ['failover', 'name', failover_name])
+ self.cli_set(base_path + ['failover', 'remote', failover_remote])
+ self.cli_set(base_path + ['failover', 'status', 'primary'])
+
+ # check validate() - failover needs to be enabled for at least one subnet
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(pool + ['enable-failover'])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(DHCPD_CONF)
+
+ self.assertIn(f'failover peer "{failover_name}"' + r' {', config)
+ self.assertIn(f'primary;', config)
+ self.assertIn(f'mclt 1800;', config)
+ self.assertIn(f'mclt 1800;', config)
+ self.assertIn(f'split 128;', config)
+ self.assertIn(f'port 520;', config)
+ self.assertIn(f'peer port 520;', config)
+ self.assertIn(f'max-response-delay 30;', config)
+ self.assertIn(f'max-unacked-updates 10;', config)
+ self.assertIn(f'load balance max seconds 3;', config)
+ self.assertIn(f'peer port 520;', config)
+ self.assertIn(f'address {failover_local};', config)
+ self.assertIn(f'peer address {failover_remote};', config)
+
+ network = address_from_cidr(subnet)
+ netmask = netmask_from_cidr(subnet)
+ self.assertIn(f'ddns-update-style none;', config)
+ self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
+ self.assertIn(f'option routers {router};', config)
+ self.assertIn(f'range {range_0_start} {range_0_stop};', config)
+ self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+ self.assertIn(f'failover peer "{failover_name}";', config)
+ self.assertIn(f'deny dynamic bootp clients;', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-relay.py b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
index 5a9dd1aa6..fc206435b 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.template import address_from_cidr
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py
index 3f9564e59..7177f1505 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-server.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-server.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.template import inc_ip
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index d8a87ffd4..fd1bd055c 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -20,7 +20,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py
index 8005eb319..44e27828d 100755
--- a/smoketest/scripts/cli/test_service_dns_forwarding.py
+++ b/smoketest/scripts/cli/test_service_dns_forwarding.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import read_file
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 3ed7655e9..d2e708384 100755
--- a/smoketest/scripts/cli/test_service_https.py
+++ b/smoketest/scripts/cli/test_service_https.py
@@ -17,7 +17,6 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import run
base_path = ['service', 'https']
diff --git a/smoketest/scripts/cli/test_service_mdns-repeater.py b/smoketest/scripts/cli/test_service_mdns-repeater.py
index b1092c3e5..c6efd1b6c 100755
--- a/smoketest/scripts/cli/test_service_mdns-repeater.py
+++ b/smoketest/scripts/cli/test_service_mdns-repeater.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import process_named_running
base_path = ['service', 'mdns', 'repeater']
diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py
index 26b4626c2..4875fb5d1 100755
--- a/smoketest/scripts/cli/test_service_router-advert.py
+++ b/smoketest/scripts/cli/test_service_router-advert.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import read_file
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py
index 008271102..058835c72 100755
--- a/smoketest/scripts/cli/test_service_snmp.py
+++ b/smoketest/scripts/cli/test_service_snmp.py
@@ -19,9 +19,9 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
+from vyos.template import address_from_cidr
from vyos.util import read_file
from vyos.util import process_named_running
@@ -36,16 +36,29 @@ def get_config_value(key):
return tmp[0]
class TestSNMPService(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ # delete testing SNMP config
self.cli_delete(base_path)
+ self.cli_commit()
def test_snmp_basic(self):
+ dummy_if = 'dum7312'
+ dummy_addr = '100.64.0.1/32'
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', dummy_addr])
+
# Check if SNMP can be configured and service runs
clients = ['192.0.2.1', '2001:db8::1']
networks = ['192.0.2.128/25', '2001:db8:babe::/48']
- listen = ['127.0.0.1', '::1']
+ listen = ['127.0.0.1', '::1', address_from_cidr(dummy_addr)]
+ port = '5000'
for auth in ['ro', 'rw']:
community = 'VyOS' + auth
@@ -56,7 +69,7 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['community', community, 'network', network])
for addr in listen:
- self.cli_set(base_path + ['listen-address', addr])
+ self.cli_set(base_path + ['listen-address', addr, 'port', port])
self.cli_set(base_path + ['contact', 'maintainers@vyos.io'])
self.cli_set(base_path + ['location', 'qemu'])
@@ -68,16 +81,18 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
# thus we need to transfor this into a proper list
config = get_config_value('agentaddress')
expected = 'unix:/run/snmpd.socket'
+ self.assertIn(expected, config)
+
for addr in listen:
if is_ipv4(addr):
- expected += ',udp:{}:161'.format(addr)
+ expected = f'udp:{addr}:{port}'
else:
- expected += ',udp6:[{}]:161'.format(addr)
-
- self.assertTrue(expected in config)
+ expected = f'udp6:[{addr}]:{port}'
+ self.assertIn(expected, config)
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
def test_snmpv3_sha(self):
@@ -86,7 +101,7 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002'])
self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro'])
- # check validate() - a view must be created before this can be comitted
+ # check validate() - a view must be created before this can be committed
with self.assertRaises(ConfigSessionError):
self.cli_commit()
@@ -152,4 +167,3 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index 01b875867..6f58ce3d3 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -20,7 +20,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py
index aed4c6beb..1a1bf0cdf 100755
--- a/smoketest/scripts/cli/test_service_tftp-server.py
+++ b/smoketest/scripts/cli/test_service_tftp-server.py
@@ -19,7 +19,6 @@ import unittest
from psutil import process_iter
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import read_file
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py
index d47bd452d..d12cc7d58 100755
--- a/smoketest/scripts/cli/test_service_webproxy.py
+++ b/smoketest/scripts/cli/test_service_webproxy.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_system_acceleration_qat.py b/smoketest/scripts/cli/test_system_acceleration_qat.py
index 0a86f58b8..894ea73ff 100755
--- a/smoketest/scripts/cli/test_system_acceleration_qat.py
+++ b/smoketest/scripts/cli/test_system_acceleration_qat.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
base_path = ['system', 'acceleration', 'qat']
diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py
index a2380981b..b2934cf04 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import cmd
from vyos.util import read_file
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
new file mode 100755
index 000000000..a2b5b1481
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'uacctd'
+base_path = ['system', 'flow-accounting']
+
+uacctd_conf = '/etc/pmacct/uacctd.conf'
+
+class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # after service removal process must no longer run
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_basic(self):
+ buffer_size = '5' # MiB
+ self.cli_set(base_path + ['buffer-size', buffer_size])
+
+ # You need to configure at least one interface for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ tmp = cmd('sudo iptables-save -t raw')
+ for interface in Section.interfaces('ethernet'):
+ self.assertIn(f'-A VYATTA_CT_PREROUTING_HOOK -i {interface} -m comment --comment FLOW_ACCOUNTING_RULE -j NFLOG --nflog-group 2 --nflog-size 128 --nflog-threshold 100', tmp)
+
+ uacctd = read_file(uacctd_conf)
+ # circular queue size - buffer_size
+ tmp = int(buffer_size) *1024 *1024
+ self.assertIn(f'plugin_pipe_size: {tmp}', uacctd)
+ # transfer buffer size - recommended value from pmacct developers 1/1000 of pipe size
+ tmp = int(buffer_size) *1024 *1024
+ # do an integer division
+ tmp //= 1000
+ self.assertIn(f'plugin_buffer_size: {tmp}', uacctd)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index e98a4e234..83df9d99e 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -17,7 +17,6 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import read_file
base_path = ['system', 'ip']
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index c9c9e833d..1325d4b39 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -17,7 +17,6 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import read_file
base_path = ['system', 'ipv6']
diff --git a/smoketest/scripts/cli/test_system_lcd.py b/smoketest/scripts/cli/test_system_lcd.py
index 7a39e2986..831fba979 100755
--- a/smoketest/scripts/cli/test_system_lcd.py
+++ b/smoketest/scripts/cli/test_system_lcd.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from configparser import ConfigParser
-from vyos.configsession import ConfigSession
from vyos.util import process_named_running
config_file = '/run/LCDd/LCDd.conf'
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 8327235fb..69a06eeac 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -24,14 +24,25 @@ from distutils.version import LooseVersion
from platform import release as kernel_version
from subprocess import Popen, PIPE
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import read_file
from vyos.template import inc_ip
base_path = ['system', 'login']
-users = ['vyos1', 'vyos2']
+users = ['vyos1', 'vyos-roxx123', 'VyOS-123_super.Nice']
+
+ssh_pubkey = """
+AAAAB3NzaC1yc2EAAAADAQABAAABgQD0NuhUOEtMIKnUVFIHoFatqX/c4mjerXyF
+TlXYfVt6Ls2NZZsUSwHbnhK4BKDrPvVZMW/LycjQPzWW6TGtk6UbZP1WqdviQ9hP
+jsEeKJSTKciMSvQpjBWyEQQPXSKYQC7ryQQilZDqnJgzqwzejKEe+nhhOdBvjuZc
+uukxjT69E0UmWAwLxzvfiurwiQaC7tG+PwqvtfHOPL3i6yRO2C5ORpFarx8PeGDS
+IfIXJCr3LoUbLHeuE7T2KaOKQcX0UsWJ4CoCapRLpTVYPDB32BYfgq7cW1Sal1re
+EGH2PzuXBklinTBgCHA87lHjpwDIAqdmvMj7SXIW9LxazLtP+e37sexE7xEs0cpN
+l68txdDbY2P2Kbz5mqGFfCvBYKv9V2clM5vyWNy/Xp5TsCis89nn83KJmgFS7sMx
+pHJz8umqkxy3hfw0K7BRFtjWd63sbOP8Q/SDV7LPaIfIxenA9zv2rY7y+AIqTmSr
+TTSb0X1zPGxPIRFy5GoGtO9Mm5h4OZk=
+"""
class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
@@ -42,6 +53,8 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_add_linux_system_user(self):
+ # We are not allowed to re-use a username already taken by the Linux
+ # base system
system_user = 'backup'
self.cli_set(base_path + ['user', system_user, 'authentication', 'plaintext-password', system_user])
@@ -75,9 +88,30 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
(stdout, stderr) = proc.communicate()
# stdout is something like this:
- # b'Linux vyos 4.19.101-amd64-vyos #1 SMP Sun Feb 2 10:18:07 UTC 2020 x86_64 GNU/Linux\n'
+ # b'Linux LR1.wue3 5.10.61-amd64-vyos #1 SMP Fri Aug 27 08:55:46 UTC 2021 x86_64 GNU/Linux\n'
self.assertTrue(len(stdout) > 40)
+ def test_system_user_ssh_key(self):
+ ssh_user = 'ssh-test_user'
+ public_keys = 'vyos_test@domain-foo.com'
+ type = 'ssh-rsa'
+
+ self.cli_set(base_path + ['user', ssh_user, 'authentication', 'public-keys', public_keys, 'key', ssh_pubkey.replace('\n','')])
+
+ # check validate() - missing type for public-key
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['user', ssh_user, 'authentication', 'public-keys', public_keys, 'type', type])
+
+ self.cli_commit()
+
+ # Check that SSH key was written properly
+ tmp = cmd(f'sudo cat /home/{ssh_user}/.ssh/authorized_keys')
+ key = f'{type} ' + ssh_pubkey.replace('\n','')
+ self.assertIn(key, tmp)
+
+ self.cli_delete(base_path + ['user', ssh_user])
+
def test_radius_kernel_features(self):
# T2886: RADIUS requires some Kernel options to be present
kernel = platform.release()
@@ -201,4 +235,4 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertTrue(tmp)
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2, failfast=True)
diff --git a/smoketest/scripts/cli/test_system_nameserver.py b/smoketest/scripts/cli/test_system_nameserver.py
index 50dc466c2..58c84988e 100755
--- a/smoketest/scripts/cli/test_system_nameserver.py
+++ b/smoketest/scripts/cli/test_system_nameserver.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import read_file
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index 2b86ebd7c..e8cc64463 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -19,7 +19,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.template import address_from_cidr
from vyos.template import netmask_from_cidr
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index bf528c8b7..492d01c4d 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.util import process_named_running
OCSERV_CONF = '/run/ocserv/ocserv.conf'
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 01d2e8c39..0f006ca3c 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -23,7 +23,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from netifaces import interfaces
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index cdee72e09..a8cef5ebf 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -148,20 +148,35 @@ def verify(dhcp):
'At least one DHCP shared network must be configured.')
# Inspect shared-network/subnet
- failover_names = []
listen_ok = False
subnets = []
+ failover_ok = False
+ shared_networks = len(dhcp['shared_network_name'])
+ disabled_shared_networks = 0
+
# A shared-network requires a subnet definition
for network, network_config in dhcp['shared_network_name'].items():
+ if 'disable' in network_config:
+ disabled_shared_networks += 1
+
if 'subnet' not in network_config:
raise ConfigError(f'No subnets defined for {network}. At least one\n' \
'lease subnet must be configured.')
for subnet, subnet_config in network_config['subnet'].items():
- if 'static_route' in subnet_config and len(subnet_config['static_route']) != 2:
- raise ConfigError('Missing DHCP static-route parameter(s):\n' \
- 'destination-subnet | router must be defined!')
+ # All delivered static routes require a next-hop to be set
+ if 'static_route' in subnet_config:
+ for route, route_option in subnet_config['static_route'].items():
+ if 'next_hop' not in route_option:
+ raise ConfigError(f'DHCP static-route "{route}" requires router to be defined!')
+
+ # DHCP failover needs at least one subnet that uses it
+ if 'enable_failover' in subnet_config:
+ if 'failover' not in dhcp:
+ raise ConfigError(f'Can not enable failover for "{subnet}" in "{network}".\n' \
+ 'Failover is not configured globally!')
+ failover_ok = True
# Check if DHCP address range is inside configured subnet declaration
if 'range' in subnet_config:
@@ -191,23 +206,6 @@ def verify(dhcp):
tmp = IPRange(range_config['start'], range_config['stop'])
networks.append(tmp)
- if 'failover' in subnet_config:
- for key in ['local_address', 'peer_address', 'name', 'status']:
- if key not in subnet_config['failover']:
- raise ConfigError(f'Missing DHCP failover parameter "{key}"!')
-
- # Failover names must be uniquie
- if subnet_config['failover']['name'] in failover_names:
- name = subnet_config['failover']['name']
- raise ConfigError(f'DHCP failover names must be unique:\n' \
- f'{name} has already been configured!')
- failover_names.append(subnet_config['failover']['name'])
-
- # Failover requires start/stop ranges for pool
- if 'range' not in subnet_config:
- raise ConfigError(f'DHCP failover requires at least one start-stop range to be configured\n'\
- f'within shared-network "{network}, {subnet}" for using failover!')
-
# Exclude addresses must be in bound
if 'exclude' in subnet_config:
for exclude in subnet_config['exclude']:
@@ -234,7 +232,7 @@ def verify(dhcp):
# There must be one subnet connected to a listen interface.
# This only counts if the network itself is not disabled!
if 'disable' not in network_config:
- if is_subnet_connected(subnet, primary=True):
+ if is_subnet_connected(subnet, primary=False):
listen_ok = True
# Subnets must be non overlapping
@@ -251,6 +249,19 @@ def verify(dhcp):
if net.overlaps(net2):
raise ConfigError('Conflicting subnet ranges: "{net}" overlaps "{net2}"!')
+ # Prevent 'disable' for shared-network if only one network is configured
+ if (shared_networks - disabled_shared_networks) < 1:
+ raise ConfigError(f'At least one shared network must be active!')
+
+ if 'failover' in dhcp:
+ if not failover_ok:
+ raise ConfigError('DHCP failover must be enabled for at least one subnet!')
+
+ for key in ['name', 'remote', 'source_address', 'status']:
+ if key not in dhcp['failover']:
+ tmp = key.replace('_', '-')
+ raise ConfigError(f'DHCP failover requires "{tmp}" to be specified!')
+
for address in (dict_search('listen_address', dhcp) or []):
if is_addr_assigned(address):
listen_ok = True
diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index 175300bb0..e6a2e4486 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -128,7 +128,7 @@ def verify(dhcpv6):
# Subnets must be unique
if subnet in subnets:
- raise ConfigError('DHCPv6 subnets must be unique! Subnet {0} defined multiple times!'.format(subnet['network']))
+ raise ConfigError(f'DHCPv6 subnets must be unique! Subnet {subnet} defined multiple times!')
subnets.append(subnet)
# DHCPv6 requires at least one configured address range or one static mapping
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index c44e6c974..06366362a 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -66,21 +66,6 @@ def get_config(config=None):
if conf.exists(base_nameservers_dhcp):
dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)})
- # Split the source_address property into separate IPv4 and IPv6 lists
- # NOTE: In future versions of pdns-recursor (> 4.4.0), this logic can be removed
- # as both IPv4 and IPv6 addresses can be specified in a single setting.
- source_address_v4 = []
- source_address_v6 = []
-
- for source_address in dns['source_address']:
- if is_ipv6(source_address):
- source_address_v6.append(source_address)
- else:
- source_address_v4.append(source_address)
-
- dns.update({'source_address_v4': source_address_v4})
- dns.update({'source_address_v6': source_address_v6})
-
return dns
def verify(dns):
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index 9cae29481..0a4559ade 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -306,7 +306,7 @@ def verify(config):
source_ip_presented = True
break
if not source_ip_presented:
- raise ConfigError("Your \"netflow source-ip\" does not exist in the system")
+ print("Warning: your \"netflow source-ip\" does not exist in the system")
# check if engine-id compatible with selected protocol version
if config['netflow']['engine-id']:
diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py
index f4c75c257..a7135911d 100755
--- a/src/conf_mode/host_name.py
+++ b/src/conf_mode/host_name.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -14,10 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-conf-mode script for 'system host-name' and 'system domain-name'.
-"""
-
import re
import sys
import copy
@@ -25,10 +21,13 @@ import copy
import vyos.util
import vyos.hostsd_client
-from vyos.config import Config
from vyos import ConfigError
-from vyos.util import cmd, call, process_named_running
-
+from vyos.config import Config
+from vyos.ifconfig import Section
+from vyos.template import is_ip
+from vyos.util import cmd
+from vyos.util import call
+from vyos.util import process_named_running
from vyos import airbag
airbag.enable()
@@ -37,7 +36,7 @@ default_config_data = {
'domain_name': '',
'domain_search': [],
'nameserver': [],
- 'nameservers_dhcp_interfaces': [],
+ 'nameservers_dhcp_interfaces': {},
'static_host_mapping': {}
}
@@ -51,29 +50,37 @@ def get_config(config=None):
hosts = copy.deepcopy(default_config_data)
- hosts['hostname'] = conf.return_value("system host-name")
+ hosts['hostname'] = conf.return_value(['system', 'host-name'])
# This may happen if the config is not loaded yet,
# e.g. if run by cloud-init
if not hosts['hostname']:
hosts['hostname'] = default_config_data['hostname']
- if conf.exists("system domain-name"):
- hosts['domain_name'] = conf.return_value("system domain-name")
+ if conf.exists(['system', 'domain-name']):
+ hosts['domain_name'] = conf.return_value(['system', 'domain-name'])
hosts['domain_search'].append(hosts['domain_name'])
- for search in conf.return_values("system domain-search domain"):
+ for search in conf.return_values(['system', 'domain-search', 'domain']):
hosts['domain_search'].append(search)
- hosts['nameserver'] = conf.return_values("system name-server")
+ if conf.exists(['system', 'name-server']):
+ for ns in conf.return_values(['system', 'name-server']):
+ if is_ip(ns):
+ hosts['nameserver'].append(ns)
+ else:
+ tmp = ''
+ if_type = Section.section(ns)
+ if conf.exists(['interfaces', if_type, ns, 'address']):
+ tmp = conf.return_values(['interfaces', if_type, ns, 'address'])
- hosts['nameservers_dhcp_interfaces'] = conf.return_values("system name-servers-dhcp")
+ hosts['nameservers_dhcp_interfaces'].update({ ns : tmp })
# system static-host-mapping
- for hn in conf.list_nodes('system static-host-mapping host-name'):
+ for hn in conf.list_nodes(['system', 'static-host-mapping', 'host-name']):
hosts['static_host_mapping'][hn] = {}
- hosts['static_host_mapping'][hn]['address'] = conf.return_value(f'system static-host-mapping host-name {hn} inet')
- hosts['static_host_mapping'][hn]['aliases'] = conf.return_values(f'system static-host-mapping host-name {hn} alias')
+ hosts['static_host_mapping'][hn]['address'] = conf.return_value(['system', 'static-host-mapping', 'host-name', hn, 'inet'])
+ hosts['static_host_mapping'][hn]['aliases'] = conf.return_values(['system', 'static-host-mapping', 'host-name', hn, 'alias'])
return hosts
@@ -103,8 +110,10 @@ def verify(hosts):
if not hostname_regex.match(a) and len(a) != 0:
raise ConfigError(f'Invalid alias "{a}" in static-host-mapping "{host}"')
- # TODO: add warnings for nameservers_dhcp_interfaces if interface doesn't
- # exist or doesn't have address dhcp(v6)
+ for interface, interface_config in hosts['nameservers_dhcp_interfaces'].items():
+ # Warnin user if interface does not have DHCP or DHCPv6 configured
+ if not set(interface_config).intersection(['dhcp', 'dhcpv6']):
+ print(f'WARNING: "{interface}" is not a DHCP interface but uses DHCP name-server option!')
return None
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index 472eb77e4..7e4b117c8 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -19,6 +19,7 @@
import sys
import os
import json
+import time
from copy import deepcopy
import vyos.defaults
@@ -34,11 +35,6 @@ config_file = '/etc/vyos/http-api.conf'
vyos_conf_scripts_dir=vyos.defaults.directories['conf_mode']
-# XXX: this model will need to be extended for tag nodes
-dependencies = [
- 'https.py',
-]
-
def get_config(config=None):
http_api = deepcopy(vyos.defaults.api_data)
x = http_api.get('api_keys')
@@ -103,8 +99,10 @@ def apply(http_api):
else:
call('systemctl stop vyos-http-api.service')
- for dep in dependencies:
- cmd(f'{vyos_conf_scripts_dir}/{dep}', raising=ConfigError)
+ # Let uvicorn settle before restarting Nginx
+ time.sleep(2)
+
+ cmd(f'{vyos_conf_scripts_dir}/https.py', raising=ConfigError)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 5b0046a72..4d3ebc587 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -18,7 +18,6 @@ import os
from sys import exit
from netifaces import interfaces
-import re
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -41,26 +40,6 @@ 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
@@ -80,12 +59,6 @@ def get_config(config=None):
bridge['member'].update({'interface_remove': tmp })
else:
bridge.update({'member': {'interface_remove': tmp }})
-
-
- # 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:
@@ -99,7 +72,6 @@ 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'])
- 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])
@@ -120,19 +92,11 @@ 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:
- 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' : ''})
+ tmp = has_vlan_subinterface_configured(conf,interface)
+ if 'enable_vlan' in bridge and tmp:
+ bridge['member']['interface'][interface].update({'has_vlan' : ''})
return bridge
@@ -142,8 +106,8 @@ def verify(bridge):
verify_dhcpv6(bridge)
verify_vrf(bridge)
-
- vlan_aware = False
+
+ ifname = bridge['ifname']
if dict_search('member.interface', bridge):
for interface, interface_config in bridge['member']['interface'].items():
@@ -166,31 +130,24 @@ 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!')
-
- # VLAN-aware bridge members must not have VLAN interface configuration
- if 'native_vlan' in interface_config:
- vlan_aware = True
-
- if 'allowed_vlan' in interface_config:
- vlan_aware = True
-
- if vlan_aware and 'wlan' in interface:
- raise ConfigError(error_msg + 'VLAN aware cannot be set!')
-
- 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')
+
+ if 'enable_vlan' in bridge:
+ if 'has_vlan' in interface_config:
+ raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!')
+
+ if 'wlan' in interface:
+ raise ConfigError(error_msg + 'VLAN aware cannot be set!')
+ else:
+ for option in ['allowed_vlan', 'native_vlan']:
+ if option in interface_config:
+ raise ConfigError('Can not use VLAN options on non VLAN aware bridge')
+
+ if 'enable_vlan' in bridge:
+ if dict_search('vif.1', bridge):
+ raise ConfigError(f'VLAN 1 sub interface cannot be set for VLAN aware bridge {ifname}, and VLAN 1 is always the parent interface')
+ else:
+ if dict_search('vif', bridge):
+ raise ConfigError(f'You must first activate "enable-vlan" of {ifname} bridge to use "vif"')
return None
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 349b0e7a3..de851262b 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -62,12 +62,6 @@ def verify(ethernet):
ifname = ethernet['ifname']
verify_interface_exists(ifname)
-
- # No need to check speed and duplex keys as both have default values.
- if ((ethernet['speed'] == 'auto' and ethernet['duplex'] != 'auto') or
- (ethernet['speed'] != 'auto' and ethernet['duplex'] == 'auto')):
- raise ConfigError('Speed/Duplex missmatch. Must be both auto or manually configured')
-
verify_mtu(ethernet)
verify_mtu_ipv6(ethernet)
verify_dhcpv6(ethernet)
@@ -76,25 +70,31 @@ def verify(ethernet):
verify_eapol(ethernet)
verify_mirror(ethernet)
- # verify offloading capabilities
- if dict_search('offload.rps', ethernet) != None:
- if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'):
- raise ConfigError('Interface does not suport RPS!')
+ ethtool = Ethtool(ifname)
+ # No need to check speed and duplex keys as both have default values.
+ if ((ethernet['speed'] == 'auto' and ethernet['duplex'] != 'auto') or
+ (ethernet['speed'] != 'auto' and ethernet['duplex'] == 'auto')):
+ raise ConfigError('Speed/Duplex missmatch. Must be both auto or manually configured')
- driver = EthernetIf(ifname).get_driver_name()
- # T3342 - Xen driver requires special treatment
- if driver == 'vif':
- if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None:
- raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\
- 'for MTU size larger then 1500 bytes')
+ if ethernet['speed'] != 'auto' and ethernet['duplex'] != 'auto':
+ # We need to verify if the requested speed and duplex setting is
+ # supported by the underlaying NIC.
+ speed = ethernet['speed']
+ duplex = ethernet['duplex']
+ if not ethtool.check_speed_duplex(speed, duplex):
+ raise ConfigError(f'Adapter does not support changing speed and duplex '\
+ f'settings to: {speed}/{duplex}!')
+
+ if 'disable_flow_control' in ethernet:
+ if not ethtool.check_flow_control():
+ raise ConfigError('Adapter does not support changing flow-control settings!')
- ethtool = Ethtool(ifname)
if 'ring_buffer' in ethernet:
- max_rx = ethtool.get_rx_buffer()
+ max_rx = ethtool.get_ring_buffer_max('rx')
if not max_rx:
raise ConfigError('Driver does not support RX ring-buffer configuration!')
- max_tx = ethtool.get_tx_buffer()
+ max_tx = ethtool.get_ring_buffer_max('tx')
if not max_tx:
raise ConfigError('Driver does not support TX ring-buffer configuration!')
@@ -108,6 +108,18 @@ def verify(ethernet):
raise ConfigError(f'Driver only supports a maximum TX ring-buffer '\
f'size of "{max_tx}" bytes!')
+ # verify offloading capabilities
+ if dict_search('offload.rps', ethernet) != None:
+ if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'):
+ raise ConfigError('Interface does not suport RPS!')
+
+ driver = ethtool.get_driver_name()
+ # T3342 - Xen driver requires special treatment
+ if driver == 'vif':
+ if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None:
+ raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\
+ 'for MTU size larger then 1500 bytes')
+
if {'is_bond_member', 'mac'} <= set(ethernet):
print(f'WARNING: changing mac address "{mac}" will be ignored as "{ifname}" '
f'is a member of bond "{is_bond_member}"'.format(**ethernet))
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 0a420f7bf..ae35ed3c4 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -40,6 +40,7 @@ from vyos.util import call
from vyos.util import chown
from vyos.util import chmod_600
from vyos.util import dict_search
+from vyos.util import makedir
from vyos.validate import is_addr_assigned
from vyos import ConfigError
@@ -50,6 +51,7 @@ user = 'openvpn'
group = 'openvpn'
cfg_file = '/run/openvpn/{ifname}.conf'
+service_file = '/run/systemd/system/openvpn@{ifname}.service.d/20-override.conf'
def checkCertHeader(header, filename):
"""
@@ -79,9 +81,6 @@ def get_config(config=None):
openvpn = get_interface_dict(conf, base)
openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn)
- openvpn['daemon_user'] = user
- openvpn['daemon_group'] = group
-
return openvpn
def verify(openvpn):
@@ -425,6 +424,10 @@ def verify(openvpn):
def generate(openvpn):
interface = openvpn['ifname']
directory = os.path.dirname(cfg_file.format(**openvpn))
+ # create base config directory on demand
+ makedir(directory, user, group)
+ # enforce proper permissions on /run/openvpn
+ chown(directory, user, group)
# we can't know in advance which clients have been removed,
# thus all client configs will be removed and re-added on demand
@@ -432,22 +435,28 @@ def generate(openvpn):
if os.path.isdir(ccd_dir):
rmtree(ccd_dir, ignore_errors=True)
+ # Remove systemd directories with overrides
+ service_dir = os.path.dirname(service_file.format(**openvpn))
+ if os.path.isdir(service_dir):
+ rmtree(service_dir, ignore_errors=True)
+
if 'deleted' in openvpn or 'disable' in openvpn:
return None
# create client config directory on demand
- if not os.path.exists(ccd_dir):
- os.makedirs(ccd_dir, 0o755)
- chown(ccd_dir, user, group)
+ makedir(ccd_dir, user, group)
- # Fix file permissons for keys
- fix_permissions = []
+ # Fix file permissons for site2site shared secret
+ if dict_search('shared_secret_key_file', openvpn):
+ chmod_600(openvpn['shared_secret_key_file'])
+ chown(openvpn['shared_secret_key_file'], user, group)
- 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)
+ # Fix file permissons for TLS certificate and keys
+ for tls in ['auth_file', 'ca_cert_file', 'cert_file', 'crl_file',
+ 'crypt_file', 'dh_file', 'key_file']:
+ if dict_search(f'tls.{tls}', openvpn):
+ chmod_600(openvpn['tls'][tls])
+ chown(openvpn['tls'][tls], user, group)
# Generate User/Password authentication file
if 'authentication' in openvpn:
@@ -474,18 +483,20 @@ def generate(openvpn):
render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn,
formater=lambda _: _.replace("&quot;", '"'), user=user, group=group)
- # Fixup file permissions
- for file in fix_permissions:
- chmod_600(file)
+ # Render 20-override.conf for OpenVPN service
+ render(service_file.format(**openvpn), 'openvpn/service-override.conf.tmpl', openvpn,
+ formater=lambda _: _.replace("&quot;", '"'), user=user, group=group)
+ # Reload systemd services config to apply an override
+ call(f'systemctl daemon-reload')
return None
def apply(openvpn):
interface = openvpn['ifname']
- call(f'systemctl stop openvpn@{interface}.service')
# Do some cleanup when OpenVPN is disabled/deleted
if 'deleted' in openvpn or 'disable' in openvpn:
+ call(f'systemctl stop openvpn@{interface}.service')
for cleanup_file in glob(f'/run/openvpn/{interface}.*'):
if os.path.isfile(cleanup_file):
os.unlink(cleanup_file)
@@ -497,7 +508,7 @@ def apply(openvpn):
# No matching OpenVPN process running - maybe it got killed or none
# existed - nevertheless, spawn new OpenVPN process
- call(f'systemctl start openvpn@{interface}.service')
+ call(f'systemctl reload-or-restart openvpn@{interface}.service')
conf = VTunIf.get_config()
conf['device_type'] = openvpn['device_type']
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index e5958e9ae..2798d321f 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -18,6 +18,7 @@ import os
from sys import exit
from netifaces import interfaces
+from ipaddress import IPv4Address
from vyos.config import Config
from vyos.configdict import dict_merge
@@ -31,6 +32,7 @@ from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vrf
from vyos.configverify import verify_tunnel
from vyos.ifconfig import Interface
+from vyos.ifconfig import Section
from vyos.ifconfig import TunnelIf
from vyos.template import is_ipv4
from vyos.template import is_ipv6
@@ -74,6 +76,41 @@ def verify(tunnel):
verify_tunnel(tunnel)
+ # If tunnel source address any and key not set
+ if tunnel['encapsulation'] in ['gre'] and \
+ dict_search('source_address', tunnel) == '0.0.0.0' and \
+ dict_search('parameters.ip.key', tunnel) == None:
+ raise ConfigError('Tunnel parameters ip key must be set!')
+
+ if tunnel['encapsulation'] in ['gre', 'gretap']:
+ if dict_search('parameters.ip.key', tunnel) != None:
+ # Check pairs tunnel source-address/encapsulation/key with exists tunnels.
+ # Prevent the same key for 2 tunnels with same source-address/encap. T2920
+ for tunnel_if in Section.interfaces('tunnel'):
+ # It makes no sense to run the test for re-used GRE keys on our
+ # own interface we are currently working on
+ if tunnel['ifname'] == tunnel_if:
+ continue
+ tunnel_cfg = get_interface_config(tunnel_if)
+ # no match on encapsulation - bail out
+ if dict_search('linkinfo.info_kind', tunnel_cfg) != tunnel['encapsulation']:
+ continue
+ new_source_address = dict_search('source_address', tunnel)
+ # Convert tunnel key to ip key, format "ip -j link show"
+ # 1 => 0.0.0.1, 999 => 0.0.3.231
+ orig_new_key = dict_search('parameters.ip.key', tunnel)
+ new_key = IPv4Address(int(orig_new_key))
+ new_key = str(new_key)
+ if dict_search('address', tunnel_cfg) == new_source_address and \
+ dict_search('linkinfo.info_data.ikey', tunnel_cfg) == new_key:
+ raise ConfigError(f'Key "{orig_new_key}" for source-address "{new_source_address}" ' \
+ f'is already used for tunnel "{tunnel_if}"!')
+
+ # Keys are not allowed with ipip and sit tunnels
+ if tunnel['encapsulation'] in ['ipip', 'sit']:
+ if dict_search('parameters.ip.key', tunnel) != None:
+ raise ConfigError('Keys are not allowed with ipip and sit tunnels!')
+
verify_mtu_ipv6(tunnel)
verify_address(tunnel)
verify_vrf(tunnel)
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index 31c599145..cb46b3723 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -25,7 +25,9 @@ from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_vrf
from vyos.ifconfig import WWANIf
from vyos.util import cmd
+from vyos.util import call
from vyos.util import dict_search
+from vyos.util import DEVNULL
from vyos.template import render
from vyos import ConfigError
from vyos import airbag
@@ -89,7 +91,7 @@ def apply(wwan):
options += ',user={user},password={password}'.format(**wwan['authentication'])
command = f'{base_cmd} --simple-connect="{options}"'
- cmd(command)
+ call(command, stdout=DEVNULL)
w.update(wwan)
return None
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
index 7ca2d9b44..771f635a0 100755
--- a/src/conf_mode/ipsec-settings.py
+++ b/src/conf_mode/ipsec-settings.py
@@ -18,7 +18,9 @@ import re
import os
from time import sleep
-from sys import exit
+
+# Top level import so that configd can override it
+from sys import argv
from vyos.config import Config
from vyos import ConfigError
@@ -216,6 +218,20 @@ def generate(data):
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
+def is_charon_responsive():
+ # Check if charon responds to strokes
+ #
+ # Sometimes it takes time to fully initialize,
+ # so waiting for the process to come to live isn't always enough
+ #
+ # There's no official "no-op" stroke so we use the "memusage" stroke as a substitute
+ from os import system
+ res = system("ipsec stroke memusage >&/dev/null")
+ if res == 0:
+ return True
+ else:
+ return False
+
def restart_ipsec():
try:
# Restart the IPsec daemon when it's running.
@@ -223,17 +239,28 @@ def restart_ipsec():
# there's a chance that this script will run before charon is up,
# so we can't assume it's running and have to check and wait if needed.
- # First, wait for charon to get started by the old ipsec.pl script.
+ # But before everything else, there's a catch!
+ # This script is run from _two_ places: "vpn ipsec options" and the top level "vpn" node
+ # When IPsec isn't set up yet, and a user wants to commit an IPsec config with some
+ # "vpn ipsec settings", this script will first be called before StrongSWAN is started by vpn-config.pl!
+ # Thus if this script is run from "settings" _and_ charon is unresponsive,
+ # we shouldn't wait for it, else there will be a deadlock.
+ # We indicate that by running the script under vyshim from "vpn ipsec options" (which sets a variable named "argv")
+ # and running it without configd from "vpn ipsec"
+ if "from-options" in argv:
+ if not is_charon_responsive():
+ return
+
+ # If we got this far, then we actually need to restart StrongSWAN
+
+ # First, wait for charon to get started by the old vpn-config.pl script.
from time import sleep, time
from os import system
now = time()
while True:
if (time() - now) > 60:
raise OSError("Timeout waiting for the IPsec process to become responsive")
- # There's no oficial "no-op" stroke,
- # so we use memusage to check if charon is alive and responsive
- res = system("ipsec stroke memusage >&/dev/null")
- if res == 0:
+ if is_charon_responsive():
break
sleep(5)
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index da91f3b11..0c179b724 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -19,12 +19,16 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.configdict import node_changed
-from vyos import ConfigError
-from vyos.util import call
+from vyos.configverify import verify_common_route_maps
+from vyos.configverify import verify_interface_exists
+from vyos.ifconfig import Interface
from vyos.util import dict_search
-from vyos.template import render
+from vyos.util import get_interface_config
from vyos.template import render_to_string
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()
@@ -34,126 +38,172 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['protocols', 'isis']
- isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ base = ['protocols', 'isis']
+ isis = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True)
+
+ interfaces_removed = node_changed(conf, base + ['interface'])
+ if interfaces_removed:
+ isis['interface_removed'] = list(interfaces_removed)
+
+ # Bail out early if configuration tree does not exist
+ if not conf.exists(base):
+ isis.update({'deleted' : ''})
+ return isis
+
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ # XXX: Note that we can not call defaults(base), as defaults does not work
+ # on an instance of a tag node.
+ default_values = defaults(base)
+ # merge in default values
+ isis = dict_merge(default_values, isis)
+
+ # We also need some additional information from the config, prefix-lists
+ # and route-maps for instance. They will be used in verify().
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = conf.get_config_dict(['policy'])
+ # Merge policy dict into "regular" config dict
+ isis = dict_merge(tmp, isis)
return isis
def verify(isis):
# bail out early - looks like removal from running config
- if not isis:
+ if not isis or 'deleted' in isis:
return None
- for process, isis_config in isis.items():
- # If more then one isis process is defined (Frr only supports one)
- # http://docs.frrouting.org/en/latest/isisd.html#isis-router
- if len(isis) > 1:
- raise ConfigError('Only one isis process can be defined')
-
- # If network entity title (net) not defined
- if 'net' not in isis_config:
- raise ConfigError('ISIS net format iso is mandatory!')
-
- # If interface not set
- if 'interface' not in isis_config:
- raise ConfigError('ISIS interface is mandatory!')
-
- # If md5 and plaintext-password set at the same time
- if 'area_password' in isis_config:
- if {'md5', 'plaintext_password'} <= set(isis_config['encryption']):
- raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!')
-
- # If one param from delay set, but not set others
- if 'spf_delay_ietf' in isis_config:
- required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
- exist_timers = []
- for elm_timer in required_timers:
- if elm_timer in isis_config['spf_delay_ietf']:
- exist_timers.append(elm_timer)
-
- exist_timers = set(required_timers).difference(set(exist_timers))
- if len(exist_timers) > 0:
- raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
-
- # If Redistribute set, but level don't set
- if 'redistribute' in isis_config:
- proc_level = isis_config.get('level','').replace('-','_')
- for proto, proto_config in isis_config.get('redistribute', {}).get('ipv4', {}).items():
+ if 'net' not in isis:
+ raise ConfigError('Network entity is mandatory!')
+
+ # last byte in IS-IS area address must be 0
+ tmp = isis['net'].split('.')
+ if int(tmp[-1]) != 0:
+ raise ConfigError('Last byte of IS-IS network entity title must always be 0!')
+
+ verify_common_route_maps(isis)
+
+ # If interface not set
+ if 'interface' not in isis:
+ raise ConfigError('Interface used for routing updates is mandatory!')
+
+ for interface in isis['interface']:
+ verify_interface_exists(interface)
+ # Interface MTU must be >= configured lsp-mtu
+ mtu = Interface(interface).get_mtu()
+ area_mtu = isis['lsp_mtu']
+ # Recommended maximum PDU size = interface MTU - 3 bytes
+ recom_area_mtu = mtu - 3
+ if mtu < int(area_mtu) or int(area_mtu) > recom_area_mtu:
+ raise ConfigError(f'Interface {interface} has MTU {mtu}, ' \
+ f'current area MTU is {area_mtu}! \n' \
+ f'Recommended area lsp-mtu {recom_area_mtu} or less ' \
+ '(calculated on MTU size).')
+
+ # If md5 and plaintext-password set at the same time
+ for password in ['area_password', 'domain_password']:
+ if password in isis:
+ if {'md5', 'plaintext_password'} <= set(isis[password]):
+ tmp = password.replace('_', '-')
+ raise ConfigError(f'Can use either md5 or plaintext-password for {tmp}!')
+
+ # If one param from delay set, but not set others
+ if 'spf_delay_ietf' in isis:
+ required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
+ exist_timers = []
+ for elm_timer in required_timers:
+ if elm_timer in isis['spf_delay_ietf']:
+ exist_timers.append(elm_timer)
+
+ exist_timers = set(required_timers).difference(set(exist_timers))
+ if len(exist_timers) > 0:
+ raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
+
+ # If Redistribute set, but level don't set
+ if 'redistribute' in isis:
+ proc_level = isis.get('level','').replace('-','_')
+ for afi in ['ipv4', 'ipv6']:
+ if afi not in isis['redistribute']:
+ continue
+
+ for proto, proto_config in isis['redistribute'][afi].items():
if 'level_1' not in proto_config and 'level_2' not in proto_config:
- raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(process, proto))
- for redistribute_level in proto_config.keys():
- if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level:
- raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(process, proc_level, proto, redistribute_level))
-
- # Segment routing checks
- if dict_search('segment_routing', isis_config):
- if dict_search('segment_routing.global_block', isis_config):
- high_label_value = dict_search('segment_routing.global_block.high_label_value', isis_config)
- low_label_value = dict_search('segment_routing.global_block.low_label_value', isis_config)
- # If segment routing global block high value is blank, throw error
- if low_label_value and not high_label_value:
- raise ConfigError('Segment routing global block high value must not be left blank')
- # If segment routing global block low value is blank, throw error
- if high_label_value and not low_label_value:
- raise ConfigError('Segment routing global block low value must not be left blank')
- # If segment routing global block low value is higher than the high value, throw error
- if int(low_label_value) > int(high_label_value):
- raise ConfigError('Segment routing global block low value must be lower than high value')
-
- if dict_search('segment_routing.local_block', isis_config):
- high_label_value = dict_search('segment_routing.local_block.high_label_value', isis_config)
- low_label_value = dict_search('segment_routing.local_block.low_label_value', isis_config)
- # If segment routing local block high value is blank, throw error
- if low_label_value and not high_label_value:
- raise ConfigError('Segment routing local block high value must not be left blank')
- # If segment routing local block low value is blank, throw error
- if high_label_value and not low_label_value:
- raise ConfigError('Segment routing local block low value must not be left blank')
- # If segment routing local block low value is higher than the high value, throw error
- if int(low_label_value) > int(high_label_value):
- raise ConfigError('Segment routing local block low value must be lower than high value')
+ raise ConfigError(f'Redistribute level-1 or level-2 should be specified in ' \
+ f'"protocols isis {process} redistribute {afi} {proto}"!')
+
+ for redistr_level, redistr_config in proto_config.items():
+ if proc_level and proc_level != 'level_1_2' and proc_level != redistr_level:
+ raise ConfigError(f'"protocols isis {process} redistribute {afi} {proto} {redistr_level}" ' \
+ f'can not be used with \"protocols isis {process} level {proc_level}\"')
+
+ # Segment routing checks
+ if dict_search('segment_routing.global_block', isis):
+ high_label_value = dict_search('segment_routing.global_block.high_label_value', isis)
+ low_label_value = dict_search('segment_routing.global_block.low_label_value', isis)
+
+ # If segment routing global block high value is blank, throw error
+ if (low_label_value and not high_label_value) or (high_label_value and not low_label_value):
+ raise ConfigError('Segment routing global block requires both low and high value!')
+
+ # If segment routing global block low value is higher than the high value, throw error
+ if int(low_label_value) > int(high_label_value):
+ raise ConfigError('Segment routing global block low value must be lower than high value')
+
+ if dict_search('segment_routing.local_block', isis):
+ high_label_value = dict_search('segment_routing.local_block.high_label_value', isis)
+ low_label_value = dict_search('segment_routing.local_block.low_label_value', isis)
+
+ # If segment routing local block high value is blank, throw error
+ if (low_label_value and not high_label_value) or (high_label_value and not low_label_value):
+ raise ConfigError('Segment routing local block requires both high and low value!')
+
+ # If segment routing local block low value is higher than the high value, throw error
+ if int(low_label_value) > int(high_label_value):
+ raise ConfigError('Segment routing local block low value must be lower than high value')
return None
def generate(isis):
- if not isis:
- isis['new_frr_config'] = ''
+ if not isis or 'deleted' in isis:
+ isis['frr_isisd_config'] = ''
+ isis['frr_zebra_config'] = ''
return None
- # only one ISIS process is supported, so we can directly send the first key
- # of the config dict
- process = list(isis.keys())[0]
- isis[process]['process'] = process
-
- isis['new_frr_config'] = render_to_string('frr/isisd.frr.tmpl',
- isis[process])
-
+ isis['protocol'] = 'isis' # required for frr/route-map.frr.tmpl
+ isis['frr_zebra_config'] = render_to_string('frr/route-map.frr.tmpl', isis)
+ isis['frr_isisd_config'] = render_to_string('frr/isisd.frr.tmpl', isis)
return None
def apply(isis):
+ isis_daemon = 'isisd'
+ zebra_daemon = 'zebra'
+
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(daemon='isisd')
- frr_cfg.modify_section(r'interface \S+', '')
- frr_cfg.modify_section(f'router isis \S+', '')
- frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['new_frr_config'])
- frr_cfg.commit_configuration(daemon='isisd')
-
- # If FRR config is blank, rerun the blank commit x times due to frr-reload
- # behavior/bug not properly clearing out on one commit.
- if isis['new_frr_config'] == '':
- for a in range(5):
- frr_cfg.commit_configuration(daemon='isisd')
-
- # Debugging
- '''
- print('')
- print('--------- DEBUGGING ----------')
- print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
- print(f'Replacement config:\n{isis["new_frr_config"]}\n\n')
- print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
- '''
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'(\s+)?ip protocol isis route-map [-a-zA-Z0-9.]+$', '', '(\s|!)')
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
+
+ frr_cfg.load_configuration(isis_daemon)
+ frr_cfg.modify_section(f'^router isis VyOS$', '')
+
+ for key in ['interface', 'interface_removed']:
+ if key not in isis:
+ continue
+ for interface in isis[key]:
+ frr_cfg.modify_section(f'^interface {interface}$', '')
+
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['frr_isisd_config'])
+ frr_cfg.commit_configuration(isis_daemon)
+
+ # Save configuration to /run/frr/config/frr.conf
+ frr.save_configuration()
return None
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index 8ddd705f2..f36abbf90 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -125,7 +125,7 @@ def get_config(config=None):
conf.set_level(base)
- # Get distribute list interface
+ # Get distribute list interface
for dist_iface in conf.list_nodes('distribute-list interface'):
# Set level 'distribute-list interface ethX'
conf.set_level(base + ['distribute-list', 'interface', dist_iface])
@@ -301,6 +301,7 @@ def apply(rip):
if os.path.exists(config_file):
call(f'vtysh -d ripd -f {config_file}')
+ call('sudo vtysh --writeconfig --noerror')
os.remove(config_file)
else:
print("File {0} not found".format(config_file))
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 3990e5735..0fbe90cce 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -20,13 +20,17 @@ from sys import exit
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.snmpv3_hashgen import plaintext_to_md5
+from vyos.snmpv3_hashgen import plaintext_to_sha1
+from vyos.snmpv3_hashgen import random
from vyos.template import render
from vyos.template import is_ipv4
-from vyos.util import call, chmod_755
+from vyos.util import call
+from vyos.util import chmod_755
from vyos.validate import is_addr_assigned
from vyos.version import get_version_data
-from vyos import ConfigError, airbag
+from vyos import ConfigError
+from vyos import airbag
airbag.enable()
config_file_client = r'/etc/snmp/snmp.conf'
@@ -401,19 +405,20 @@ def verify(snmp):
addr = listen[0]
port = listen[1]
+ tmp = None
if is_ipv4(addr):
# example: udp:127.0.0.1:161
- listen = 'udp:' + addr + ':' + port
+ tmp = f'udp:{addr}:{port}'
elif snmp['ipv6_enabled']:
# example: udp6:[::1]:161
- listen = 'udp6:' + '[' + addr + ']' + ':' + port
+ tmp = f'udp6:[{addr}]:{port}'
# We only wan't to configure addresses that exist on the system.
# Hint the user if they don't exist
if is_addr_assigned(addr):
- snmp['listen_on'].append(listen)
+ if tmp: snmp['listen_on'].append(tmp)
else:
- print('WARNING: SNMP listen address {0} not configured!'.format(addr))
+ print(f'WARNING: SNMP listen address {addr} not configured!')
verify_vrf(snmp)
diff --git a/src/conf_mode/system-login-banner.py b/src/conf_mode/system-login-banner.py
index 569010735..2220d7b66 100755
--- a/src/conf_mode/system-login-banner.py
+++ b/src/conf_mode/system-login-banner.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -22,12 +22,13 @@ from vyos import airbag
airbag.enable()
motd="""
-The programs included with the Debian GNU/Linux system are free software;
-the exact distribution terms for each program are described in the
-individual files in /usr/share/doc/*/copyright.
+Check out project news at https://blog.vyos.io
+and feel free to report bugs at https://phabricator.vyos.net
-Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
-permitted by applicable law.
+You can change this banner using "set system login banner post-login" command.
+
+VyOS is a free software distribution that includes multiple components,
+you can check individual component licenses under /usr/share/doc/*/copyright
"""
@@ -36,7 +37,7 @@ PRELOGIN_NET_FILE = r'/etc/issue.net'
POSTLOGIN_FILE = r'/etc/motd'
default_config_data = {
- 'issue': 'Welcome to VyOS - \n \l\n',
+ 'issue': 'Welcome to VyOS - \\n \\l\n\n',
'issue_net': 'Welcome to VyOS\n',
'motd': motd
}
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 59ea1d34b..8aa43dd32 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -59,7 +59,7 @@ def get_config(config=None):
conf = Config()
base = ['system', 'login']
login = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True)
+ no_tag_node_value_mangle=True, get_first_key=True)
# users no longer existing in the running configuration need to be deleted
local_users = get_local_users()
@@ -246,7 +246,9 @@ def apply(login):
# XXX: Should we deny using root at all?
home_dir = getpwnam(user).pw_dir
render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.tmpl',
- user_config, permission=0o600, user=user, group='users')
+ user_config, permission=0o600,
+ formater=lambda _: _.replace("&quot;", '"'),
+ user=user, group='users')
except Exception as e:
raise ConfigError(f'Adding user "{user}" raised exception: "{e}"')
diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py
index 33a546bd3..19b252513 100755
--- a/src/conf_mode/system_console.py
+++ b/src/conf_mode/system_console.py
@@ -18,9 +18,14 @@ import os
import re
from vyos.config import Config
-from vyos.util import call, read_file, write_file
+from vyos.configdict import dict_merge
+from vyos.util import call
+from vyos.util import read_file
+from vyos.util import write_file
from vyos.template import render
-from vyos import ConfigError, airbag
+from vyos.xml import defaults
+from vyos import ConfigError
+from vyos import airbag
airbag.enable()
by_bus_dir = '/dev/serial/by-bus'
@@ -36,21 +41,27 @@ def get_config(config=None):
console = conf.get_config_dict(base, get_first_key=True)
# bail out early if no serial console is configured
- if 'device' not in console.keys():
+ if 'device' not in console:
return console
# convert CLI values to system values
- for device in console['device'].keys():
- # no speed setting has been configured - use default value
- if not 'speed' in console['device'][device].keys():
- tmp = { 'speed': '' }
- if device.startswith('hvc'):
- tmp['speed'] = 38400
- else:
- tmp['speed'] = 115200
+ default_values = defaults(base + ['device'])
+ for device, device_config in console['device'].items():
+ if 'speed' not in device_config and device.startswith('hvc'):
+ # XEN console has a different default console speed
+ console['device'][device]['speed'] = 38400
+ else:
+ # Merge in XML defaults - the proper way to do it
+ console['device'][device] = dict_merge(default_values,
+ console['device'][device])
+
+ return console
- console['device'][device].update(tmp)
+def verify(console):
+ if not console or 'device' not in console:
+ return None
+ for device in console['device']:
if device.startswith('usb'):
# It is much easiert to work with the native ttyUSBn name when using
# getty, but that name may change across reboots - depending on the
@@ -58,13 +69,13 @@ def get_config(config=None):
# to its dynamic device file - and create a new dict entry for it.
by_bus_device = f'{by_bus_dir}/{device}'
if os.path.isdir(by_bus_dir) and os.path.exists(by_bus_device):
- tmp = os.path.basename(os.readlink(by_bus_device))
- # updating the dict must come as last step in the loop!
- console['device'][tmp] = console['device'].pop(device)
+ device = os.path.basename(os.readlink(by_bus_device))
- return console
+ # If the device name still starts with usbXXX no matching tty was found
+ # and it can not be used as a serial interface
+ if device.startswith('usb'):
+ raise ConfigError(f'Device {device} does not support beeing used as tty')
-def verify(console):
return None
def generate(console):
@@ -76,20 +87,29 @@ def generate(console):
call(f'systemctl stop {basename}')
os.unlink(os.path.join(root, basename))
- if not console:
+ if not console or 'device' not in console:
return None
- for device in console['device'].keys():
+ for device, device_config in console['device'].items():
+ if device.startswith('usb'):
+ # It is much easiert to work with the native ttyUSBn name when using
+ # getty, but that name may change across reboots - depending on the
+ # amount of connected devices. We will resolve the fixed device name
+ # to its dynamic device file - and create a new dict entry for it.
+ by_bus_device = f'{by_bus_dir}/{device}'
+ if os.path.isdir(by_bus_dir) and os.path.exists(by_bus_device):
+ device = os.path.basename(os.readlink(by_bus_device))
+
config_file = base_dir + f'/serial-getty@{device}.service'
getty_wants_symlink = base_dir + f'/getty.target.wants/serial-getty@{device}.service'
- render(config_file, 'getty/serial-getty.service.tmpl', console['device'][device])
+ render(config_file, 'getty/serial-getty.service.tmpl', device_config)
os.symlink(config_file, getty_wants_symlink)
# GRUB
# For existing serial line change speed (if necessary)
# Only applys to ttyS0
- if 'ttyS0' not in console['device'].keys():
+ if 'ttyS0' not in console['device']:
return None
speed = console['device']['ttyS0']['speed']
@@ -98,7 +118,6 @@ def generate(console):
return None
lines = read_file(grub_config).split('\n')
-
p = re.compile(r'^(.* console=ttyS0),[0-9]+(.*)$')
write = False
newlines = []
@@ -122,9 +141,8 @@ def generate(console):
return None
def apply(console):
- # reset screen blanking
+ # Reset screen blanking
call('/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux </dev/tty1 >/dev/tty1 2>&1')
-
# Reload systemd manager configuration
call('systemctl daemon-reload')
@@ -136,11 +154,11 @@ def apply(console):
call('/usr/bin/setterm -blank 15 -powersave powerdown -powerdown 60 -term linux </dev/tty1 >/dev/tty1 2>&1')
# Start getty process on configured serial interfaces
- for device in console['device'].keys():
+ for device in console['device']:
# Only start console if it exists on the running system. If a user
# detaches a USB serial console and reboots - it should not fail!
if os.path.exists(f'/dev/{device}'):
- call(f'systemctl start serial-getty@{device}.service')
+ call(f'systemctl restart serial-getty@{device}.service')
return None
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
deleted file mode 100755
index 969266c30..000000000
--- a/src/conf_mode/vpn_ipsec.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/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
-
-from sys import exit
-
-from vyos.config import Config
-from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
-from vyos import ConfigError
-from vyos import airbag
-from pprint import pprint
-airbag.enable()
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
- base = ['vpn', 'nipsec']
- if not conf.exists(base):
- return None
-
- # retrieve common dictionary keys
- ipsec = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- return ipsec
-
-def verify(ipsec):
- if not ipsec:
- return None
-
-def generate(ipsec):
- if not ipsec:
- return None
-
- return ipsec
-
-def apply(ipsec):
- if not ipsec:
- return None
-
- pprint(ipsec)
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index e970d2ef5..86aa9af09 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -291,6 +291,8 @@ def get_config(config=None):
# LNS secret
if conf.exists(['lns', 'shared-secret']):
l2tp['lns_shared_secret'] = conf.return_value(['lns', 'shared-secret'])
+ if conf.exists(['lns', 'host-name']):
+ l2tp['lns_host_name'] = conf.return_value(['lns', 'host-name'])
if conf.exists(['ccp-disable']):
l2tp['ccp_disable'] = True
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 47367f125..070009722 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -50,16 +50,14 @@ def verify(sstp):
verify_accel_ppp_base_service(sstp)
- if not sstp['client_ip_pool']:
+ if 'client_ip_pool' not in sstp and 'client_ipv6_pool' not in sstp:
raise ConfigError('Client IP subnet required')
#
# SSL certificate checks
#
tmp = dict_search('ssl.ca_cert_file', sstp)
- if not tmp:
- raise ConfigError(f'SSL CA certificate file required!')
- else:
+ if tmp:
if not os.path.isfile(tmp):
raise ConfigError(f'SSL CA certificate "{tmp}" does not exist!')
diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py
index 3ccc7d66b..ad38adaec 100755
--- a/src/conf_mode/vrrp.py
+++ b/src/conf_mode/vrrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -17,244 +17,144 @@
import os
from sys import exit
-from ipaddress import ip_address, ip_interface, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address
-from json import dumps
-from pathlib import Path
-
-import vyos.config
-
-from vyos import ConfigError
-from vyos.util import call
-from vyos.template import render
+from ipaddress import ip_interface
+from ipaddress import IPv4Interface
+from ipaddress import IPv6Interface
+from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.ifconfig.vrrp import VRRP
-
+from vyos.template import render
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
+from vyos.util import call
+from vyos.util import is_systemd_service_running
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
def get_config(config=None):
- vrrp_groups = []
- sync_groups = []
-
if config:
- config = config
+ conf = config
else:
- config = vyos.config.Config()
-
- # Get the VRRP groups
- for group_name in config.list_nodes("high-availability vrrp group"):
- config.set_level("high-availability vrrp group {0}".format(group_name))
-
- # Retrieve the values
- group = {"preempt": True, "use_vmac": False, "disable": False}
-
- if config.exists("disable"):
- group["disable"] = True
-
- group["name"] = group_name
- group["vrid"] = config.return_value("vrid")
- group["interface"] = config.return_value("interface")
- group["description"] = config.return_value("description")
- group["advertise_interval"] = config.return_value("advertise-interval")
- group["priority"] = config.return_value("priority")
- group["hello_source"] = config.return_value("hello-source-address")
- group["peer_address"] = config.return_value("peer-address")
- group["sync_group"] = config.return_value("sync-group")
- group["preempt_delay"] = config.return_value("preempt-delay")
- group["virtual_addresses"] = config.return_values("virtual-address")
- group["virtual_addresses_excluded"] = config.return_values("virtual-address-excluded")
-
- group["auth_password"] = config.return_value("authentication password")
- group["auth_type"] = config.return_value("authentication type")
-
- group["health_check_script"] = config.return_value("health-check script")
- group["health_check_interval"] = config.return_value("health-check interval")
- group["health_check_count"] = config.return_value("health-check failure-count")
-
- group["master_script"] = config.return_value("transition-script master")
- group["backup_script"] = config.return_value("transition-script backup")
- group["fault_script"] = config.return_value("transition-script fault")
- group["stop_script"] = config.return_value("transition-script stop")
- group["script_mode_force"] = config.exists("transition-script mode-force")
-
- if config.exists("no-preempt"):
- group["preempt"] = False
- if config.exists("rfc3768-compatibility"):
- group["use_vmac"] = True
-
- # Substitute defaults where applicable
- if not group["advertise_interval"]:
- group["advertise_interval"] = 1
- if not group["priority"]:
- group["priority"] = 100
- if not group["preempt_delay"]:
- group["preempt_delay"] = 0
- if not group["health_check_interval"]:
- group["health_check_interval"] = 60
- if not group["health_check_count"]:
- group["health_check_count"] = 3
-
- # FIXUP: translate our option for auth type to keepalived's syntax
- # for simplicity
- if group["auth_type"]:
- if group["auth_type"] == "plaintext-password":
- group["auth_type"] = "PASS"
- else:
- group["auth_type"] = "AH"
-
- vrrp_groups.append(group)
-
- config.set_level("")
-
- # Get the sync group used for conntrack-sync
- conntrack_sync_group = None
- if config.exists("service conntrack-sync failover-mechanism vrrp"):
- conntrack_sync_group = config.return_value("service conntrack-sync failover-mechanism vrrp sync-group")
-
- # Get the sync groups
- for sync_group_name in config.list_nodes("high-availability vrrp sync-group"):
- config.set_level("high-availability vrrp sync-group {0}".format(sync_group_name))
-
- sync_group = {"conntrack_sync": False}
- sync_group["name"] = sync_group_name
- sync_group["members"] = config.return_values("member")
- if conntrack_sync_group:
- if conntrack_sync_group == sync_group_name:
- sync_group["conntrack_sync"] = True
-
- # add transition script configuration
- sync_group["master_script"] = config.return_value("transition-script master")
- sync_group["backup_script"] = config.return_value("transition-script backup")
- sync_group["fault_script"] = config.return_value("transition-script fault")
- sync_group["stop_script"] = config.return_value("transition-script stop")
-
- sync_groups.append(sync_group)
-
- # create a file with dict with proposed configuration
- with open("{}.temp".format(VRRP.location['vyos']), 'w') as dict_file:
- dict_file.write(dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups}))
-
- return (vrrp_groups, sync_groups)
-
-
-def verify(data):
- vrrp_groups, sync_groups = data
-
- for group in vrrp_groups:
- # Check required fields
- if not group["vrid"]:
- raise ConfigError("vrid is required but not set in VRRP group {0}".format(group["name"]))
- if not group["interface"]:
- raise ConfigError("interface is required but not set in VRRP group {0}".format(group["name"]))
- if not group["virtual_addresses"]:
- raise ConfigError("virtual-address is required but not set in VRRP group {0}".format(group["name"]))
-
- if group["auth_password"] and (not group["auth_type"]):
- raise ConfigError("authentication type is required but not set in VRRP group {0}".format(group["name"]))
-
- # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction
-
- # XXX: filter on map object is destructive, so we force it to list.
- # Additionally, filter objects always evaluate to True, empty or not,
- # so we force them to lists as well.
- vaddrs = list(map(lambda i: ip_interface(i), group["virtual_addresses"]))
- vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs))
- vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs))
-
- if vaddrs4 and vaddrs6:
- raise ConfigError("VRRP group {0} mixes IPv4 and IPv6 virtual addresses, this is not allowed. Create separate groups for IPv4 and IPv6".format(group["name"]))
-
- if vaddrs4:
- if group["hello_source"]:
- hsa = ip_address(group["hello_source"])
- if isinstance(hsa, IPv6Address):
- raise ConfigError("VRRP group {0} uses IPv4 but its hello-source-address is IPv6".format(group["name"]))
- if group["peer_address"]:
- pa = ip_address(group["peer_address"])
- if isinstance(pa, IPv6Address):
- raise ConfigError("VRRP group {0} uses IPv4 but its peer-address is IPv6".format(group["name"]))
-
- if vaddrs6:
- if group["hello_source"]:
- hsa = ip_address(group["hello_source"])
- if isinstance(hsa, IPv4Address):
- raise ConfigError("VRRP group {0} uses IPv6 but its hello-source-address is IPv4".format(group["name"]))
- if group["peer_address"]:
- pa = ip_address(group["peer_address"])
- if isinstance(pa, IPv4Address):
- raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"]))
-
- # Warn the user about the deprecated mode-force option
- if group["script_mode_force"]:
- print("""Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.""")
- print("""It's no longer necessary, so you can safely remove it from your config now.""")
-
- # Disallow same VRID on multiple interfaces
- _groups = sorted(vrrp_groups, key=(lambda x: x["interface"]))
- count = len(_groups) - 1
- index = 0
- while (index < count):
- if (_groups[index]["vrid"] == _groups[index + 1]["vrid"]) and (_groups[index]["interface"] == _groups[index + 1]["interface"]):
- raise ConfigError("VRID {0} is used in groups {1} and {2} that both use interface {3}. Groups on the same interface must use different VRIDs".format(
- _groups[index]["vrid"], _groups[index]["name"], _groups[index + 1]["name"], _groups[index]["interface"]))
- else:
- index += 1
+ conf = Config()
+
+ base = ['high-availability', 'vrrp']
+ if not conf.exists(base):
+ return None
+
+ vrrp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ if 'group' in vrrp:
+ default_values = defaults(base + ['group'])
+ for group in vrrp['group']:
+ vrrp['group'][group] = dict_merge(default_values, vrrp['group'][group])
+
+ ## Get the sync group used for conntrack-sync
+ conntrack_path = ['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group']
+ if conf.exists(conntrack_path):
+ vrrp['conntrack_sync_group'] = conf.return_value(conntrack_path)
+
+ return vrrp
+
+def verify(vrrp):
+ if not vrrp:
+ return None
+
+ used_vrid_if = []
+ if 'group' in vrrp:
+ for group, group_config in vrrp['group'].items():
+ # Check required fields
+ if 'vrid' not in group_config:
+ raise ConfigError(f'VRID is required but not set in VRRP group "{group}"')
+
+ if 'interface' not in group_config:
+ raise ConfigError(f'Interface is required but not set in VRRP group "{group}"')
+
+ if 'virtual_address' not in group_config:
+ raise ConfigError(f'Virtual IP address is required but not set in VRRP group "{group}"')
+
+ if 'authentication' in group_config:
+ if not {'password', 'type'} <= set(group_config['authentication']):
+ raise ConfigError(f'Authentication requires both type and passwortd to be set in VRRP group "{group}"')
+
+ # We can not use a VRID once per interface
+ interface = group_config['interface']
+ vrid = group_config['vrid']
+ tmp = {'interface': interface, 'vrid': vrid}
+ if tmp in used_vrid_if:
+ raise ConfigError(f'VRID "{vrid}" can only be used once on interface "{interface}"!')
+ used_vrid_if.append(tmp)
+
+ # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction
+
+ # XXX: filter on map object is destructive, so we force it to list.
+ # Additionally, filter objects always evaluate to True, empty or not,
+ # so we force them to lists as well.
+ vaddrs = list(map(lambda i: ip_interface(i), group_config['virtual_address']))
+ vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs))
+ vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs))
+
+ if vaddrs4 and vaddrs6:
+ raise ConfigError(f'VRRP group "{group}" mixes IPv4 and IPv6 virtual addresses, this is not allowed.\n' \
+ 'Create individual groups for IPv4 and IPv6!')
+ if vaddrs4:
+ if 'hello_source_address' in group_config:
+ if is_ipv6(group_config['hello_source_address']):
+ raise ConfigError(f'VRRP group "{group}" uses IPv4 but hello-source-address is IPv6!')
+
+ if 'peer_address' in group_config:
+ if is_ipv6(group_config['peer_address']):
+ raise ConfigError(f'VRRP group "{group}" uses IPv4 but peer-address is IPv6!')
+
+ if vaddrs6:
+ if 'hello_source_address' in group_config:
+ if is_ipv4(group_config['hello_source_address']):
+ raise ConfigError(f'VRRP group "{group}" uses IPv6 but hello-source-address is IPv4!')
+
+ if 'peer_address' in group_config:
+ if is_ipv4(group_config['peer_address']):
+ raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!')
+
+
+ # Warn the user about the deprecated mode-force option
+ if 'transition_script' in group_config and 'mode_force' in group_config['transition_script']:
+ print('Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.')
+ print('It is no longer necessary, so you can safely remove it from your config now.')
# Check sync groups
- vrrp_group_names = list(map(lambda x: x["name"], vrrp_groups))
-
- for sync_group in sync_groups:
- for m in sync_group["members"]:
- if not (m in vrrp_group_names):
- raise ConfigError("VRRP sync-group {0} refers to VRRP group {1}, but group {1} does not exist".format(sync_group["name"], m))
-
-
-def generate(data):
- vrrp_groups, sync_groups = data
-
- # Remove disabled groups from the sync group member lists
- for sync_group in sync_groups:
- for member in sync_group["members"]:
- g = list(filter(lambda x: x["name"] == member, vrrp_groups))[0]
- if g["disable"]:
- print("Warning: ignoring disabled VRRP group {0} in sync-group {1}".format(g["name"], sync_group["name"]))
- # Filter out disabled groups
- vrrp_groups = list(filter(lambda x: x["disable"] is not True, vrrp_groups))
-
- render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl',
- {"groups": vrrp_groups, "sync_groups": sync_groups})
- render(VRRP.location['daemon'], 'vrrp/daemon.tmpl', {})
+ if 'sync_group' in vrrp:
+ for sync_group, sync_config in vrrp['sync_group'].items():
+ if 'member' in sync_config:
+ for member in sync_config['member']:
+ if member not in vrrp['group']:
+ raise ConfigError(f'VRRP sync-group "{sync_group}" refers to VRRP group "{member}", '\
+ 'but it does not exist!')
+
+def generate(vrrp):
+ if not vrrp:
+ return None
+
+ render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl', vrrp)
return None
+def apply(vrrp):
+ service_name = 'keepalived.service'
+ if not vrrp:
+ call(f'systemctl stop {service_name}')
+ return None
-def apply(data):
- vrrp_groups, sync_groups = data
- if vrrp_groups:
- # safely rename a temporary file with configuration dict
- try:
- dict_file = Path("{}.temp".format(VRRP.location['vyos']))
- dict_file.rename(Path(VRRP.location['vyos']))
- except Exception as err:
- print("Unable to rename the file with keepalived config for FIFO pipe: {}".format(err))
-
- if not VRRP.is_running():
- print("Starting the VRRP process")
- ret = call("systemctl restart keepalived.service")
- else:
- print("Reloading the VRRP process")
- ret = call("systemctl reload keepalived.service")
-
- if ret != 0:
- raise ConfigError("keepalived failed to start")
+ # XXX: T3944 - reload keepalived configuration if service is already running
+ # to not cause any service disruption when applying changes.
+ if is_systemd_service_running(service_name):
+ call(f'systemctl reload {service_name}')
else:
- # VRRP is removed in the commit
- print("Stopping the VRRP process")
- call("systemctl stop keepalived.service")
- os.unlink(VRRP.location['daemon'])
-
+ call(f'systemctl restart {service_name}')
return None
-
if __name__ == '__main__':
try:
c = get_config()
@@ -262,5 +162,5 @@ if __name__ == '__main__':
generate(c)
apply(c)
except ConfigError as e:
- print("VRRP error: {0}".format(str(e)))
+ print(e)
exit(1)
diff --git a/src/etc/cron.d/check-wwan b/src/etc/cron.d/check-wwan
new file mode 100644
index 000000000..28190776f
--- /dev/null
+++ b/src/etc/cron.d/check-wwan
@@ -0,0 +1 @@
+*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py
diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient b/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient
index 939055a63..ae6bf9f16 100644
--- a/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient
+++ b/src/etc/dhcp/dhclient-enter-hooks.d/02-vyos-stopdhclient
@@ -2,26 +2,37 @@
if [ -z ${CONTROLLED_STOP} ] ; then
# stop dhclient for this interface, if it is not current one
# get PID for current dhclient
- current_dhclient=`ps --no-headers --format ppid --pid $$ | awk '{ print $1 }'`
+ current_dhclient=`ps --no-headers --format ppid --pid $$ | awk '{ print \$1 }'`
# get PID for master process (current can be a fork)
- master_dhclient=`ps --no-headers --format ppid --pid $current_dhclient | awk '{ print $1 }'`
+ master_dhclient=`ps --no-headers --format ppid --pid $current_dhclient | awk '{ print \$1 }'`
# get IP version for current dhclient
- ipversion_arg=`ps --no-headers --format args --pid $current_dhclient | awk '{ print $2 }'`
+ ipversion_arg=`ps --no-headers --format args --pid $current_dhclient | awk 'match(\$0, /\s-(4|6)\s/, IPV) { printf("%s", IPV[1]) }'`
# get list of all dhclient running for current interface
- dhclients_pids=(`pgrep -f "dhclient $ipversion_arg.* $interface(\s|$)"`)
+ if [[ $ipversion_arg == "6" ]]; then
+ dhclients_pids=(`pgrep -f "dhclient.*\s-6\s.*\s$interface(\s|$)"`)
+ else
+ dhclients_pids=(`ps --no-headers --format pid,args -C dhclient | awk "{ if(match(\\$0, /\s${interface}(\s|$)/) && !match(\\$0, /\s-6\s/)) printf(\"%s\n\", \\$1) }"`)
+ fi
logmsg info "Current dhclient PID: $current_dhclient, Parent PID: $master_dhclient, IP version: $ipversion_arg, All dhclients for interface $interface: ${dhclients_pids[@]}"
# stop all dhclients for current interface, except current one
for dhclient in ${dhclients_pids[@]}; do
if ([ $dhclient -ne $current_dhclient ] && [ $dhclient -ne $master_dhclient ]); then
- logmsg info "Stopping dhclient with PID: ${dhclient}"
# get path to PID-file of dhclient process
- local dhclient_pidfile=`ps --no-headers --format args --pid $dhclient | awk 'match($0, ".*-pf (/.*pid) .*", PF) { print PF[1] }'`
+ local dhclient_pidfile=`ps --no-headers --format args --pid $dhclient | awk 'match(\$0, ".*-pf (/.*pid) .*", PF) { print PF[1] }'`
+ # get path to lease-file of dhclient process
+ local dhclient_leasefile=`ps --no-headers --format args --pid $dhclient | awk 'match(\$0, ".*-lf (/\\\S*leases) .*", LF) { print LF[1] }'`
# stop dhclient with native command - this will run dhclient-script with correct reason unlike simple kill
- dhclient -e CONTROLLED_STOP=yes -x -pf $dhclient_pidfile
+ logmsg info "Stopping dhclient with PID: ${dhclient}, PID file: ${dhclient_pidfile}, Leases file: ${dhclient_leasefile}"
+ if [[ -e $dhclient_pidfile ]]; then
+ dhclient -e CONTROLLED_STOP=yes -x -pf $dhclient_pidfile -lf $dhclient_leasefile
+ else
+ logmsg error "PID file $dhclient_pidfile does not exists, killing dhclient with SIGTERM signal"
+ kill -s 15 ${dhclient}
+ fi
fi
done
fi
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
index edb7c7b27..f060c6ee8 100644
--- a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
@@ -19,7 +19,7 @@ if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then
for router in $old_routers; do
# check if we are bound to a VRF
local vrf_name=$(basename /sys/class/net/${interface}/upper_* | sed -e 's/upper_//')
- if [ -n $vrf_name ]; then
+ if [ "$vrf_name" != "*" ]; then
vrf="vrf $vrf_name"
fi
diff --git a/src/etc/systemd/system/keepalived.service.d/override.conf b/src/etc/systemd/system/keepalived.service.d/override.conf
index 9fcabf652..c18ae0c29 100644
--- a/src/etc/systemd/system/keepalived.service.d/override.conf
+++ b/src/etc/systemd/system/keepalived.service.d/override.conf
@@ -1,2 +1,12 @@
+[Unit]
+ConditionPathExists=
+ConditionPathExists=/run/keepalived/keepalived.conf
+After=
+After=vyos-router.service
+
[Service]
KillMode=process
+ExecStart=
+ExecStart=/usr/sbin/keepalived --use-file /run/keepalived/keepalived.conf --pid /run/keepalived/keepalived.pid --dont-fork --snmp
+PIDFile=
+PIDFile=/run/keepalived/keepalived.pid
diff --git a/src/etc/systemd/system/openvpn@.service.d/10-override.conf b/src/etc/systemd/system/openvpn@.service.d/10-override.conf
new file mode 100644
index 000000000..775a2d7ba
--- /dev/null
+++ b/src/etc/systemd/system/openvpn@.service.d/10-override.conf
@@ -0,0 +1,14 @@
+[Unit]
+After=
+After=vyos-router.service
+
+[Service]
+WorkingDirectory=
+WorkingDirectory=/run/openvpn
+ExecStart=
+ExecStart=/usr/sbin/openvpn --daemon openvpn-%i --config %i.conf --status %i.status 30 --writepid %i.pid
+ExecReload=/bin/kill -HUP $MAINPID
+User=openvpn
+Group=openvpn
+AmbientCapabilities=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
+CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
diff --git a/src/etc/systemd/system/openvpn@.service.d/override.conf b/src/etc/systemd/system/openvpn@.service.d/override.conf
deleted file mode 100644
index 7946484a3..000000000
--- a/src/etc/systemd/system/openvpn@.service.d/override.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[Unit]
-After=
-After=vyos-router.service
-
-[Service]
-WorkingDirectory=
-WorkingDirectory=/run/openvpn
-ExecStart=
-ExecStart=/usr/sbin/openvpn --daemon openvpn-%i --config %i.conf --status %i.status 30 --writepid %i.pid
diff --git a/src/etc/udev/rules.d/90-vyos-serial.rules b/src/etc/udev/rules.d/90-vyos-serial.rules
index 3f10f4924..5cca89e89 100644
--- a/src/etc/udev/rules.d/90-vyos-serial.rules
+++ b/src/etc/udev/rules.d/90-vyos-serial.rules
@@ -22,7 +22,7 @@ IMPORT{builtin}="path_id", IMPORT{builtin}="usb_id"
# (tr -d -) does the replacement
# - Replace the first group after ":" to represent the bus relation (sed -e 0,/:/s//b/) indicated by "b"
# - Replace the next group after ":" to represent the port relation (sed -e 0,/:/s//p/) indicated by "p"
-ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", PROGRAM="/bin/sh -c 'D=$env{ID_PATH}; echo ${D:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result"
-ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'D=$env{ID_PATH}; echo ${D:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", PROGRAM="/bin/sh -c 'echo $env{ID_PATH} | cut -d- -f3- | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'echo $env{ID_PATH} | cut -d- -f3- | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result"
LABEL="serial_end"
diff --git a/src/helpers/vyos-check-wwan.py b/src/helpers/vyos-check-wwan.py
new file mode 100755
index 000000000..c6e6c54b7
--- /dev/null
+++ b/src/helpers/vyos-check-wwan.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from vyos.configquery import VbashOpRun
+from vyos.configquery import ConfigTreeQuery
+
+from vyos.util import is_wwan_connected
+from vyos.util import call
+
+conf = ConfigTreeQuery()
+dict = conf.get_config_dict(['interfaces', 'wwan'], key_mangling=('-', '_'),
+ get_first_key=True)
+
+for interface, interface_config in dict.items():
+ if not is_wwan_connected(interface):
+ if 'disable' in interface_config:
+ # do not restart this interface as it's disabled by the user
+ continue
+
+ #op = VbashOpRun()
+ #op.run(['connect', 'interface', interface])
+ call(f'VYOS_TAGNODE_VALUE={interface} /usr/libexec/vyos/conf_mode/interfaces-wwan.py')
+
+exit(0)
diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6
new file mode 100755
index 000000000..aefe84737
--- /dev/null
+++ b/src/migration-scripts/dhcp-server/5-to-6
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T1968: allow multiple static-routes to be configured
+# T3838: rename dns-server -> name-server
+
+import sys
+from vyos.configtree import ConfigTree
+
+if (len(sys.argv) < 1):
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['service', 'dhcp-server']
+config = ConfigTree(config_file)
+
+if not config.exists(base + ['shared-network-name']):
+ # Nothing to do
+ exit(0)
+
+# Run this for every instance if 'shared-network-name'
+for network in config.list_nodes(base + ['shared-network-name']):
+ base_network = base + ['shared-network-name', network]
+
+ if not config.exists(base_network + ['subnet']):
+ continue
+
+ # Run this for every specified 'subnet'
+ for subnet in config.list_nodes(base_network + ['subnet']):
+ base_subnet = base_network + ['subnet', subnet]
+
+ # T1968: allow multiple static-routes to be configured
+ if config.exists(base_subnet + ['static-route']):
+ prefix = config.return_value(base_subnet + ['static-route', 'destination-subnet'])
+ router = config.return_value(base_subnet + ['static-route', 'router'])
+ config.delete(base_subnet + ['static-route'])
+
+ config.set(base_subnet + ['static-route', prefix, 'next-hop'], value=router)
+ config.set_tag(base_subnet + ['static-route'])
+
+ # T3838: rename dns-server -> name-server
+ if config.exists(base_subnet + ['dns-server']):
+ config.rename(base_subnet + ['dns-server'], 'name-server')
+
+
+ # T3672: ISC DHCP server only supports one failover peer
+ if config.exists(base_subnet + ['failover']):
+ # There can only be one failover configuration, if none is present
+ # we add the first one
+ if not config.exists(base + ['failover']):
+ local = config.return_value(base_subnet + ['failover', 'local-address'])
+ remote = config.return_value(base_subnet + ['failover', 'peer-address'])
+ status = config.return_value(base_subnet + ['failover', 'status'])
+ name = config.return_value(base_subnet + ['failover', 'name'])
+
+ config.set(base + ['failover', 'remote'], value=remote)
+ config.set(base + ['failover', 'source-address'], value=local)
+ config.set(base + ['failover', 'status'], value=status)
+ config.set(base + ['failover', 'name'], value=name)
+
+ config.delete(base_subnet + ['failover'])
+ config.set(base_subnet + ['enable-failover'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2
index 8c4f4b5c7..ba10c26f2 100755
--- a/src/migration-scripts/dns-forwarding/1-to-2
+++ b/src/migration-scripts/dns-forwarding/1-to-2
@@ -67,8 +67,14 @@ if config.exists(base + ['listen-on']):
# retrieve corresponding interface addresses in CIDR format
# those need to be converted in pure IP addresses without network information
path = ['interfaces', section, intf, 'address']
- for addr in config.return_values(path):
- listen_addr.append( ip_interface(addr).ip )
+ try:
+ for addr in config.return_values(path):
+ listen_addr.append( ip_interface(addr).ip )
+ except:
+ # Some interface types do not use "address" option (e.g. OpenVPN)
+ # and may not even have a fixed address
+ print("Could not retrieve the address of the interface {} from the config".format(intf))
+ print("You will need to update your DNS forwarding configuration manually")
for addr in listen_addr:
config.set(base + ['listen-address'], value=addr, replace=False)
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
new file mode 100755
index 000000000..0bd858760
--- /dev/null
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS
+# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details.
+# T3787: Remove deprecated UDP fragmentation offloading option
+
+from sys import argv
+
+from vyos.ethtool import Ethtool
+from vyos.configtree import ConfigTree
+
+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()
+
+base = ['interfaces', 'ethernet']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ exit(0)
+
+for ifname in config.list_nodes(base):
+ eth = Ethtool(ifname)
+
+ # If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'gro'])
+ enabled, fixed = eth.get_generic_receive_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'gro'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'gro'])
+
+ # If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'gso'])
+ enabled, fixed = eth.get_generic_segmentation_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'gso'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'gso'])
+
+ # If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'lro'])
+ enabled, fixed = eth.get_large_receive_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'lro'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'lro'])
+
+ # If SG is enabled by the Kernel - we reflect this on the CLI. If SG is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'sg'])
+ enabled, fixed = eth.get_scatter_gather()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'sg'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'sg'])
+
+ # If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'tso'])
+ enabled, fixed = eth.get_tcp_segmentation_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'tso'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'tso'])
+
+ # Remove deprecated UDP fragmentation offloading option
+ if config.exists(base + [ifname, 'offload', 'ufo']):
+ config.delete(base + [ifname, 'offload', 'ufo'])
+
+ # Also while processing the interface configuration, not all adapters support
+ # changing the speed and duplex settings. If the desired speed and duplex
+ # values do not work for the NIC driver, we change them back to the default
+ # value of "auto" - which will be applied if the CLI node is deleted.
+ speed_path = base + [ifname, 'speed']
+ duplex_path = base + [ifname, 'duplex']
+ # speed and duplex must always be set at the same time if not set to "auto"
+ if config.exists(speed_path) and config.exists(duplex_path):
+ speed = config.return_value(speed_path)
+ duplex = config.return_value(duplex_path)
+ if speed != 'auto' and duplex != 'auto':
+ if not eth.check_speed_duplex(speed, duplex):
+ config.delete(speed_path)
+ config.delete(duplex_path)
+
+ # Also while processing the interface configuration, not all adapters support
+ # changing disabling flow-control - or change this setting. If disabling
+ # flow-control is not supported by the NIC, we remove the setting from CLI
+ flow_control_path = base + [ifname, 'disable-flow-control']
+ if config.exists(flow_control_path):
+ if not eth.check_flow_control():
+ config.delete(flow_control_path)
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22
new file mode 100755
index 000000000..098102102
--- /dev/null
+++ b/src/migration-scripts/interfaces/21-to-22
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from sys import argv
+from vyos.configtree import ConfigTree
+
+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', 'tunnel']
+
+if not config.exists(base):
+ exit(0)
+
+for interface in config.list_nodes(base):
+ path = base + [interface, 'dhcp-interface']
+ if config.exists(path):
+ tmp = config.return_value(path)
+ config.delete(path)
+ config.set(base + [interface, 'source-interface'], value=tmp)
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/isis/0-to-1 b/src/migration-scripts/isis/0-to-1
new file mode 100755
index 000000000..93cbbbed5
--- /dev/null
+++ b/src/migration-scripts/isis/0-to-1
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+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()
+
+base = ['protocols', 'isis']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# Only one IS-IS process is supported, thus this operation is save
+isis_base = base + config.list_nodes(base)
+
+# We need a temporary copy of the config
+tmp_base = ['protocols', 'isis2']
+config.copy(isis_base, tmp_base)
+
+# Now it's save to delete the old configuration
+config.delete(base)
+
+# Rename temporary copy to new final config (IS-IS domain key is static and no
+# longer required to be set via CLI)
+config.rename(tmp_base, 'isis')
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21
new file mode 100755
index 000000000..1728995de
--- /dev/null
+++ b/src/migration-scripts/system/20-to-21
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T3795: merge "system name-servers-dhcp" into "system name-server"
+
+import os
+
+from sys import argv
+from vyos.configtree import ConfigTree
+
+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()
+
+base = ['system', 'name-servers-dhcp']
+config = ConfigTree(config_file)
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+for interface in config.return_values(base):
+ config.set(['system', 'name-server'], value=interface, replace=False)
+
+config.delete(base)
+
+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/connect_disconnect.py b/src/op_mode/connect_disconnect.py
index a773aa28e..ffc574362 100755
--- a/src/op_mode/connect_disconnect.py
+++ b/src/op_mode/connect_disconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -17,21 +17,19 @@
import os
import argparse
-from sys import exit
from psutil import process_iter
-from time import strftime, localtime, time
from vyos.util import call
+from vyos.util import DEVNULL
+from vyos.util import is_wwan_connected
-def check_interface(interface):
+def check_ppp_interface(interface):
if not os.path.isfile(f'/etc/ppp/peers/{interface}'):
- print(f'Interface {interface}: invalid!')
+ print(f'Interface {interface} does not exist!')
exit(1)
def check_ppp_running(interface):
- """
- Check if ppp process is running in the interface in question
- """
+ """ Check if PPP process is running in the interface in question """
for p in process_iter():
if "pppd" in p.name():
if interface in p.cmdline():
@@ -40,32 +38,46 @@ def check_ppp_running(interface):
return False
def connect(interface):
- """
- Connect PPP interface
- """
- check_interface(interface)
+ """ Connect dialer interface """
- # Check if interface is already dialed
- if os.path.isdir(f'/sys/class/net/{interface}'):
- print(f'Interface {interface}: already connected!')
- elif check_ppp_running(interface):
- print(f'Interface {interface}: connection is beeing established!')
+ if interface.startswith('ppp'):
+ check_ppp_interface(interface)
+ # Check if interface is already dialed
+ if os.path.isdir(f'/sys/class/net/{interface}'):
+ print(f'Interface {interface}: already connected!')
+ elif check_ppp_running(interface):
+ print(f'Interface {interface}: connection is beeing established!')
+ else:
+ print(f'Interface {interface}: connecting...')
+ call(f'systemctl restart ppp@{interface}.service')
+ elif interface.startswith('wwan'):
+ if is_wwan_connected(interface):
+ print(f'Interface {interface}: already connected!')
+ else:
+ call(f'VYOS_TAGNODE_VALUE={interface} /usr/libexec/vyos/conf_mode/interfaces-wwan.py')
else:
- print(f'Interface {interface}: connecting...')
- call(f'systemctl restart ppp@{interface}.service')
+ print(f'Unknown interface {interface}, can not connect. Aborting!')
def disconnect(interface):
- """
- Disconnect PPP interface
- """
- check_interface(interface)
+ """ Disconnect dialer interface """
- # Check if interface is already down
- if not check_ppp_running(interface):
- print(f'Interface {interface}: connection is already down')
+ if interface.startswith('ppp'):
+ check_ppp_interface(interface)
+
+ # Check if interface is already down
+ if not check_ppp_running(interface):
+ print(f'Interface {interface}: connection is already down')
+ else:
+ print(f'Interface {interface}: disconnecting...')
+ call(f'systemctl stop ppp@{interface}.service')
+ elif interface.startswith('wwan'):
+ if not is_wwan_connected(interface):
+ print(f'Interface {interface}: connection is already down')
+ else:
+ modem = interface.lstrip('wwan')
+ call(f'mmcli --modem {modem} --simple-disconnect', stdout=DEVNULL)
else:
- print(f'Interface {interface}: disconnecting...')
- call(f'systemctl stop ppp@{interface}.service')
+ print(f'Unknown interface {interface}, can not disconnect. Aborting!')
def main():
parser = argparse.ArgumentParser()
diff --git a/src/op_mode/generate_ipsec_debug_archive.sh b/src/op_mode/generate_ipsec_debug_archive.sh
new file mode 100755
index 000000000..53d0a6eaa
--- /dev/null
+++ b/src/op_mode/generate_ipsec_debug_archive.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+# Collecting IPSec Debug Information
+
+DATE=`date +%d-%m-%Y`
+
+a_CMD=(
+ "sudo ipsec status"
+ "sudo swanctl -L"
+ "sudo swanctl -l"
+ "sudo swanctl -P"
+ "sudo ip x sa show"
+ "sudo ip x policy show"
+ "sudo ip tunnel show"
+ "sudo ip address"
+ "sudo ip rule show"
+ "sudo ip route"
+ "sudo ip route show table 220"
+ )
+
+
+echo "DEBUG: ${DATE} on host \"$(hostname)\"" > /tmp/ipsec-status-${DATE}.txt
+date >> /tmp/ipsec-status-${DATE}.txt
+
+# Execute all DEBUG commands and save it to file
+for cmd in "${a_CMD[@]}"; do
+ echo -e "\n### ${cmd} ###" >> /tmp/ipsec-status-${DATE}.txt
+ ${cmd} >> /tmp/ipsec-status-${DATE}.txt 2>/dev/null
+done
+
+# Collect charon logs, build .tgz archive
+sudo journalctl /usr/lib/ipsec/charon > /tmp/journalctl-charon-${DATE}.txt && \
+sudo tar -zcvf /tmp/ipsec-debug-${DATE}.tgz /tmp/journalctl-charon-${DATE}.txt /tmp/ipsec-status-${DATE}.txt >& /dev/null
+sudo rm -f /tmp/journalctl-charon-${DATE}.txt /tmp/ipsec-status-${DATE}.txt
+
+echo "Debug file is generated and located in /tmp/ipsec-debug-${DATE}.tgz"
diff --git a/src/op_mode/ping.py b/src/op_mode/ping.py
index 2144ab53c..60bbc0c78 100755
--- a/src/op_mode/ping.py
+++ b/src/op_mode/ping.py
@@ -62,8 +62,8 @@ options = {
},
'interface': {
'ping': '{command} -I {value}',
- 'type': '<interface> <X.X.X.X> <h:h:h:h:h:h:h:h>',
- 'help': 'Interface to use as source for ping'
+ 'type': '<interface>',
+ 'help': 'Source interface'
},
'interval': {
'ping': '{command} -i {value}',
@@ -115,6 +115,10 @@ options = {
'type': '<bytes>',
'help': 'Number of bytes to send'
},
+ 'source-address': {
+ 'ping': '{command} -I {value}',
+ 'type': '<x.x.x.x> <h:h:h:h:h:h:h:h>',
+ },
'ttl': {
'ping': '{command} -t {value}',
'type': '<ttl>',
@@ -234,4 +238,4 @@ if __name__ == '__main__':
# print(f'{command} {host}')
os.system(f'{command} {host}')
- \ No newline at end of file
+
diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py
index c000d7d06..ffcb865a8 100755
--- a/src/op_mode/powerctrl.py
+++ b/src/op_mode/powerctrl.py
@@ -92,37 +92,40 @@ def cancel_shutdown():
try:
run('/sbin/shutdown -c --no-wall')
except OSError as e:
- exit("Could not cancel a reboot or poweroff: %s" % e)
+ exit(f'Could not cancel a reboot or poweroff: {e}')
- message = 'Scheduled {} has been cancelled {}'.format(output['MODE'], timenow)
+ mode = output['MODE']
+ message = f'Scheduled {mode} has been cancelled {timenow}'
run(f'wall {message} > /dev/null 2>&1')
else:
print("Reboot or poweroff is not scheduled")
def execute_shutdown(time, reboot=True, ask=True):
+ action = "reboot" if reboot else "poweroff"
if not ask:
- action = "reboot" if reboot else "poweroff"
- if not ask_yes_no("Are you sure you want to %s this system?" % action):
+ if not ask_yes_no(f"Are you sure you want to {action} this system?"):
exit(0)
-
- action = "-r" if reboot else "-P"
+ action_cmd = "-r" if reboot else "-P"
if len(time) == 0:
# T870 legacy reboot job support
chk_vyatta_based_reboots()
###
- out = cmd(f'/sbin/shutdown {action} now', stderr=STDOUT)
+ out = cmd(f'/sbin/shutdown {action_cmd} now', stderr=STDOUT)
print(out.split(",", 1)[0])
return
elif len(time) == 1:
# Assume the argument is just time
ts = parse_time(time[0])
if ts:
- cmd(f'/sbin/shutdown {action} {time[0]}', stderr=STDOUT)
+ cmd(f'/sbin/shutdown {action_cmd} {time[0]}', stderr=STDOUT)
+ # Inform all other logged in users about the reboot/shutdown
+ wall_msg = f'System {action} is scheduled {time[0]}'
+ cmd(f'/usr/bin/wall "{wall_msg}"')
else:
- exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0]))
+ exit(f'Invalid time "{time[0]}". The valid format is HH:MM')
elif len(time) == 2:
# Assume it's date and time
ts = parse_time(time[0])
@@ -131,14 +134,18 @@ def execute_shutdown(time, reboot=True, ask=True):
t = datetime.combine(ds, ts)
td = t - datetime.now()
t2 = 1 + int(td.total_seconds())//60 # Get total minutes
- cmd('/sbin/shutdown {action} {t2}', stderr=STDOUT)
+
+ cmd(f'/sbin/shutdown {action_cmd} {t2}', stderr=STDOUT)
+ # Inform all other logged in users about the reboot/shutdown
+ wall_msg = f'System {action} is scheduled {time[1]} {time[0]}'
+ cmd(f'/usr/bin/wall "{wall_msg}"')
else:
if not ts:
- exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0]))
+ exit(f'Invalid time "{time[0]}". The valid format is HH:MM')
else:
- exit("Invalid time \"{0}\". A valid format is YYYY-MM-DD [HH:MM]".format(time[1]))
+ exit(f'Invalid date "{time[1]}". A valid format is YYYY-MM-DD [HH:MM]')
else:
- exit("Could not decode date and time. Valids formats are HH:MM or YYYY-MM-DD HH:MM")
+ exit('Could not decode date and time. Valids formats are HH:MM or YYYY-MM-DD HH:MM')
check_shutdown()
diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py
index d1b66b33f..109c8dd7b 100755
--- a/src/op_mode/restart_frr.py
+++ b/src/op_mode/restart_frr.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,16 +13,19 @@
#
# 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 os
import argparse
import logging
-from logging.handlers import SysLogHandler
-from pathlib import Path
import psutil
+from logging.handlers import SysLogHandler
+from shutil import rmtree
+
from vyos.util import call
+from vyos.util import ask_yes_no
+from vyos.util import process_named_running
+from vyos.util import makedir
# some default values
watchfrr = '/usr/lib/frr/watchfrr.sh'
@@ -40,40 +43,45 @@ logger.setLevel(logging.INFO)
def _check_safety():
try:
# print warning
- answer = input("WARNING: This is a potentially unsafe function! You may lose the connection to the router or active configuration after running this command. Use it at your own risk! Continue? [y/N]: ")
- if not answer.lower() == "y":
- logger.error("User aborted command")
+ if not ask_yes_no('WARNING: This is a potentially unsafe function!\n' \
+ 'You may lose the connection to the router or active configuration after\n' \
+ 'running this command. Use it at your own risk!\n\n'
+ 'Continue?'):
return False
# check if another restart process already running
if len([process for process in psutil.process_iter(attrs=['pid', 'name', 'cmdline']) if 'python' in process.info['name'] and 'restart_frr.py' in process.info['cmdline'][1]]) > 1:
- logger.error("Another restart_frr.py already running")
- answer = input("Another restart_frr.py process is already running. It is unsafe to continue. Do you want to process anyway? [y/N]: ")
- if not answer.lower() == "y":
+ message = 'Another restart_frr.py process is already running!'
+ logger.error(message)
+ if not ask_yes_no(f'\n{message} It is unsafe to continue.\n\n' \
+ 'Do you want to process anyway?'):
return False
# check if watchfrr.sh is running
- for process in psutil.process_iter(attrs=['pid', 'name', 'cmdline']):
- if 'bash' in process.info['name'] and watchfrr in process.info['cmdline']:
- logger.error("Another {} already running".format(watchfrr))
- answer = input("Another {} process is already running. It is unsafe to continue. Do you want to process anyway? [y/N]: ".format(watchfrr))
- if not answer.lower() == "y":
- return False
+ tmp = os.path.basename(watchfrr)
+ if process_named_running(tmp):
+ message = f'Another {tmp} process is already running.'
+ logger.error(message)
+ if not ask_yes_no(f'{message} It is unsafe to continue.\n\n' \
+ 'Do you want to process anyway?'):
+ return False
# check if vtysh is running
- for process in psutil.process_iter(attrs=['pid', 'name', 'cmdline']):
- if 'vtysh' in process.info['name']:
- logger.error("The vtysh is running by another task")
- answer = input("The vtysh is running by another task. It is unsafe to continue. Do you want to process anyway? [y/N]: ")
- if not answer.lower() == "y":
- return False
+ if process_named_running('vtysh'):
+ message = 'vtysh process is executed by another task.'
+ logger.error(message)
+ if not ask_yes_no(f'{message} It is unsafe to continue.\n\n' \
+ 'Do you want to process anyway?'):
+ return False
# check if temporary directory exists
- if Path(frrconfig_tmp).exists():
- logger.error("The temporary directory \"{}\" already exists".format(frrconfig_tmp))
- answer = input("The temporary directory \"{}\" already exists. It is unsafe to continue. Do you want to process anyway? [y/N]: ".format(frrconfig_tmp))
- if not answer.lower() == "y":
+ if os.path.exists(frrconfig_tmp):
+ message = f'Temporary directory "{frrconfig_tmp}" already exists!'
+ logger.error(message)
+ if not ask_yes_no(f'{message} It is unsafe to continue.\n\n' \
+ 'Do you want to process anyway?'):
return False
+
except:
logger.error("Something goes wrong in _check_safety()")
return False
@@ -84,94 +92,68 @@ def _check_safety():
# write active config to file
def _write_config():
# create temporary directory
- Path(frrconfig_tmp).mkdir(parents=False, exist_ok=True)
+ makedir(frrconfig_tmp)
# save frr.conf to it
- command = "{} -n -w --config_dir {} 2> /dev/null".format(vtysh, frrconfig_tmp)
+ command = f'{vtysh} -n -w --config_dir {frrconfig_tmp} 2> /dev/null'
return_code = call(command)
- if not return_code == 0:
- logger.error("Failed to save active config: \"{}\" returned exit code: {}".format(command, return_code))
+ if return_code != 0:
+ logger.error(f'Failed to save active config: "{command}" returned exit code: {return_code}')
return False
- logger.info("Active config saved to {}".format(frrconfig_tmp))
+ logger.info(f'Active config saved to {frrconfig_tmp}')
return True
# clear and remove temporary directory
def _cleanup():
- tmpdir = Path(frrconfig_tmp)
- try:
- if tmpdir.exists():
- for file in tmpdir.iterdir():
- file.unlink()
- tmpdir.rmdir()
- except:
- logger.error("Failed to remove temporary directory {}".format(frrconfig_tmp))
- print("Failed to remove temporary directory {}".format(frrconfig_tmp))
-
-# check if daemon is running
-def _daemon_check(daemon):
- command = "{} print_status {}".format(watchfrr, daemon)
- return_code = call(command)
- if not return_code == 0:
- logger.error("Daemon \"{}\" is not running".format(daemon))
- return False
-
- # return True if all checks were passed
- return True
+ if os.path.isdir(frrconfig_tmp):
+ rmtree(frrconfig_tmp)
# restart daemon
def _daemon_restart(daemon):
- command = "{} restart {}".format(watchfrr, daemon)
+ command = f'{watchfrr} restart {daemon}'
return_code = call(command)
if not return_code == 0:
- logger.error("Failed to restart daemon \"{}\"".format(daemon))
+ logger.error(f'Failed to restart daemon "{daemon}"!')
return False
# return True if restarted successfully
- logger.info("Daemon \"{}\" restarted".format(daemon))
+ logger.info(f'Daemon "{daemon}" restarted!')
return True
# reload old config
def _reload_config(daemon):
if daemon != '':
- command = "{} -n -b --config_dir {} -d {} 2> /dev/null".format(vtysh, frrconfig_tmp, daemon)
+ command = f'{vtysh} -n -b --config_dir {frrconfig_tmp} -d {daemon} 2> /dev/null'
else:
- command = "{} -n -b --config_dir {} 2> /dev/null".format(vtysh, frrconfig_tmp)
+ command = f'{vtysh} -n -b --config_dir {frrconfig_tmp} 2> /dev/null'
return_code = call(command)
if not return_code == 0:
- logger.error("Failed to reinstall configuration")
+ logger.error('Failed to re-install configuration!')
return False
# return True if restarted successfully
- logger.info("Configuration reinstalled successfully")
- return True
-
-# check all daemons if they are running
-def _check_args_daemon(daemons):
- for daemon in daemons:
- if not _daemon_check(daemon):
- return False
+ logger.info('Configuration re-installed successfully!')
return True
# define program arguments
cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons')
cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons')
-cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons')
+cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons')
# parse arguments
cmd_args = cmd_args_parser.parse_args()
-
# main logic
# restart daemon
if cmd_args.action == 'restart':
# check if it is safe to restart FRR
if not _check_safety():
print("\nOne of the safety checks was failed or user aborted command. Exiting.")
- sys.exit(1)
+ exit(1)
if not _write_config():
print("Failed to save active config")
_cleanup()
- sys.exit(1)
+ exit(1)
# a little trick to make further commands more clear
if not cmd_args.daemon:
@@ -179,19 +161,20 @@ if cmd_args.action == 'restart':
# check all daemons if they are running
if cmd_args.daemon != ['']:
- if not _check_args_daemon(cmd_args.daemon):
- print("Warning: some of listed daemons are not running")
+ for daemon in cmd_args.daemon:
+ if not process_named_running(daemon):
+ print('WARNING: some of listed daemons are not running!')
# run command to restart daemon
for daemon in cmd_args.daemon:
if not _daemon_restart(daemon):
- print("Failed to restart daemon: {}".format(daemon))
+ print('Failed to restart daemon: {daemon}')
_cleanup()
- sys.exit(1)
+ exit(1)
# reinstall old configuration
_reload_config(daemon)
# cleanup after all actions
_cleanup()
-sys.exit(0)
+exit(0)
diff --git a/src/op_mode/show_dhcp.py b/src/op_mode/show_dhcp.py
index ff1e3cc56..9f65b1bd6 100755
--- a/src/op_mode/show_dhcp.py
+++ b/src/op_mode/show_dhcp.py
@@ -178,7 +178,7 @@ if __name__ == '__main__':
group = parser.add_mutually_exclusive_group()
group.add_argument("-l", "--leases", action="store_true", help="Show DHCP leases")
group.add_argument("-s", "--statistics", action="store_true", help="Show DHCP statistics")
- group.add_argument("--allowed", type=str, choices=["pool", "sort", "state"], help="Show allowed values for argument")
+ group.add_argument("--allowed", type=str, choices=["sort", "state"], help="Show allowed values for argument")
parser.add_argument("-p", "--pool", type=str, help="Show lease for specific pool")
parser.add_argument("-S", "--sort", type=str, default='ip', help="Sort by")
@@ -189,11 +189,7 @@ if __name__ == '__main__':
conf = Config()
- if args.allowed == 'pool':
- if conf.exists_effective('service dhcp-server'):
- print(' '.join(conf.list_effective_nodes("service dhcp-server shared-network-name")))
- exit(0)
- elif args.allowed == 'sort':
+ if args.allowed == 'sort':
print(' '.join(lease_display_fields.keys()))
exit(0)
elif args.allowed == 'state':
diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py
index 79bb8e2a6..281d25e30 100755
--- a/src/op_mode/show_interfaces.py
+++ b/src/op_mode/show_interfaces.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2017, 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2017-2021 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
@@ -19,9 +19,7 @@ import os
import re
import sys
import glob
-import datetime
import argparse
-import netifaces
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
@@ -54,27 +52,27 @@ def filtered_interfaces(ifnames, iftypes, vif, vrrp):
ifnames: a list of interfaces names to consider, empty do not filter
return an instance of the interface class
"""
- allnames = Section.interfaces()
+ if isinstance(iftypes, list):
+ for iftype in iftypes:
+ yield from filtered_interfaces(ifnames, iftype, vif, vrrp)
- vrrp_interfaces = VRRP.active_interfaces() if vrrp else []
-
- for ifname in allnames:
+ for ifname in Section.interfaces(iftypes):
+ # Bail out early if interface name not part of our search list
if ifnames and ifname not in ifnames:
continue
- # return the class which can handle this interface name
- klass = Section.klass(ifname)
- # connect to the interface
- interface = klass(ifname, create=False, debug=False)
-
- if iftypes and interface.definition['section'] not in iftypes:
- continue
+ # As we are only "reading" from the interface - we must use the
+ # generic base class which exposes all the data via a common API
+ interface = Interface(ifname, create=False, debug=False)
+ # VLAN interfaces have a '.' in their name by convention
if vif and not '.' in ifname:
continue
- if vrrp and ifname not in vrrp_interfaces:
- continue
+ if vrrp:
+ vrrp_interfaces = VRRP.active_interfaces()
+ if ifname not in vrrp_interfaces:
+ continue
yield interface
@@ -87,10 +85,8 @@ def split_text(text, used=0):
used: number of characted already used in the screen
"""
no_tty = call('tty -s')
- if no_tty:
- return text.split()
- returned = cmd('stty size')
+ returned = cmd('stty size') if not no_tty else ''
if len(returned) == 2:
rows, columns = [int(_) for _ in returned]
else:
diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py
index 8b8f11947..beb632fa8 100755
--- a/src/op_mode/show_ipsec_sa.py
+++ b/src/op_mode/show_ipsec_sa.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -29,38 +29,22 @@ def convert(text):
def alphanum_key(key):
return [convert(c) for c in re.split('([0-9]+)', str(key))]
-try:
- session = vici.Session()
- sas = session.list_sas()
-except PermissionError:
- print("You do not have a permission to connect to the IPsec daemon")
- sys.exit(1)
-except ConnectionRefusedError:
- print("IPsec is not runing")
- sys.exit(1)
-except Exception as e:
- print("An error occured: {0}".format(e))
- sys.exit(1)
-
-sa_data = []
-
-for sa in sas:
- # list_sas() returns a list of single-item dicts
- for peer in sa:
- parent_sa = sa[peer]
- child_sas = parent_sa["child-sas"]
- installed_sas = {k: v for k, v in child_sas.items() if v["state"] == b"INSTALLED"}
-
- # parent_sa["state"] = IKE state, child_sas["state"] = ESP state
+def format_output(conns, sas):
+ sa_data = []
+
+ for peer, parent_conn in conns.items():
+ if peer not in sas:
+ continue
+
+ parent_sa = sas[peer]
+ child_sas = parent_sa['child-sas']
+ installed_sas = {v['name'].decode(): v for k, v in child_sas.items() if v["state"] == b"INSTALLED"}
+
+ state = 'down'
+ uptime = 'N/A'
+
if parent_sa["state"] == b"ESTABLISHED" and installed_sas:
state = "up"
- else:
- state = "down"
-
- if state == "up":
- uptime = vyos.util.seconds_to_human(parent_sa["established"].decode())
- else:
- uptime = "N/A"
remote_host = parent_sa["remote-host"].decode()
remote_id = parent_sa["remote-id"].decode()
@@ -69,51 +53,79 @@ for sa in sas:
remote_id = "N/A"
# The counters can only be obtained from the child SAs
- if not installed_sas:
- data = [peer, state, "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]
- sa_data.append(data)
- else:
- for csa in installed_sas:
- isa = installed_sas[csa]
- csa_name = isa['name']
- csa_name = csa_name.decode()
-
- bytes_in = hurry.filesize.size(int(isa["bytes-in"].decode()))
- bytes_out = hurry.filesize.size(int(isa["bytes-out"].decode()))
- bytes_str = "{0}/{1}".format(bytes_in, bytes_out)
-
- pkts_in = hurry.filesize.size(int(isa["packets-in"].decode()), system=hurry.filesize.si)
- pkts_out = hurry.filesize.size(int(isa["packets-out"].decode()), system=hurry.filesize.si)
- pkts_str = "{0}/{1}".format(pkts_in, pkts_out)
- # Remove B from <1K values
- pkts_str = re.sub(r'B', r'', pkts_str)
-
- enc = isa["encr-alg"].decode()
- if "encr-keysize" in isa:
- key_size = isa["encr-keysize"].decode()
- else:
- key_size = ""
- if "integ-alg" in isa:
- hash = isa["integ-alg"].decode()
- else:
- hash = ""
- if "dh-group" in isa:
- dh_group = isa["dh-group"].decode()
- else:
- dh_group = ""
-
- proposal = enc
- if key_size:
- proposal = "{0}_{1}".format(proposal, key_size)
- if hash:
- proposal = "{0}/{1}".format(proposal, hash)
- if dh_group:
- proposal = "{0}/{1}".format(proposal, dh_group)
-
- data = [csa_name, state, uptime, bytes_str, pkts_str, remote_host, remote_id, proposal]
+ for child_conn in parent_conn['children']:
+ if child_conn not in installed_sas:
+ data = [child_conn, "down", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]
sa_data.append(data)
-
-headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"]
-sa_data = sorted(sa_data, key=alphanum_key)
-output = tabulate.tabulate(sa_data, headers)
-print(output)
+ continue
+
+ isa = installed_sas[child_conn]
+ csa_name = isa['name']
+ csa_name = csa_name.decode()
+
+ bytes_in = hurry.filesize.size(int(isa["bytes-in"].decode()))
+ bytes_out = hurry.filesize.size(int(isa["bytes-out"].decode()))
+ bytes_str = "{0}/{1}".format(bytes_in, bytes_out)
+
+ pkts_in = hurry.filesize.size(int(isa["packets-in"].decode()), system=hurry.filesize.si)
+ pkts_out = hurry.filesize.size(int(isa["packets-out"].decode()), system=hurry.filesize.si)
+ pkts_str = "{0}/{1}".format(pkts_in, pkts_out)
+ # Remove B from <1K values
+ pkts_str = re.sub(r'B', r'', pkts_str)
+
+ uptime = vyos.util.seconds_to_human(isa['install-time'].decode())
+
+ enc = isa["encr-alg"].decode()
+ if "encr-keysize" in isa:
+ key_size = isa["encr-keysize"].decode()
+ else:
+ key_size = ""
+ if "integ-alg" in isa:
+ hash = isa["integ-alg"].decode()
+ else:
+ hash = ""
+ if "dh-group" in isa:
+ dh_group = isa["dh-group"].decode()
+ else:
+ dh_group = ""
+
+ proposal = enc
+ if key_size:
+ proposal = "{0}_{1}".format(proposal, key_size)
+ if hash:
+ proposal = "{0}/{1}".format(proposal, hash)
+ if dh_group:
+ proposal = "{0}/{1}".format(proposal, dh_group)
+
+ data = [csa_name, state, uptime, bytes_str, pkts_str, remote_host, remote_id, proposal]
+ sa_data.append(data)
+ return sa_data
+
+if __name__ == '__main__':
+ try:
+ session = vici.Session()
+ conns = {}
+ sas = {}
+
+ for conn in session.list_conns():
+ for key in conn:
+ conns[key] = conn[key]
+
+ for sa in session.list_sas():
+ for key in sa:
+ sas[key] = sa[key]
+
+ headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"]
+ sa_data = format_output(conns, sas)
+ sa_data = sorted(sa_data, key=alphanum_key)
+ output = tabulate.tabulate(sa_data, headers)
+ print(output)
+ except PermissionError:
+ print("You do not have a permission to connect to the IPsec daemon")
+ sys.exit(1)
+ except ConnectionRefusedError:
+ print("IPsec is not runing")
+ sys.exit(1)
+ except Exception as e:
+ print("An error occured: {0}".format(e))
+ sys.exit(1)
diff --git a/src/op_mode/show_system_integrity.py b/src/op_mode/show_system_integrity.py
deleted file mode 100755
index c34d41e80..000000000
--- a/src/op_mode/show_system_integrity.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-
-import sys
-import os
-import re
-import json
-from datetime import datetime, timedelta
-
-version_file = r'/usr/share/vyos/version.json'
-
-
-def _get_sys_build_version():
- if not os.path.exists(version_file):
- return None
- buf = open(version_file, 'r').read()
- j = json.loads(buf)
- if not 'built_on' in j:
- return None
- return datetime.strptime(j['built_on'], '%a %d %b %Y %H:%M %Z')
-
-
-def _check_pkgs(build_stamp):
- pkg_diffs = {
- 'buildtime': str(build_stamp),
- 'pkg': {}
- }
-
- pkg_info = os.listdir('/var/lib/dpkg/info/')
- for file in pkg_info:
- if re.search('\.list$', file):
- fts = os.stat('/var/lib/dpkg/info/' + file).st_mtime
- dt_str = (datetime.utcfromtimestamp(
- fts).strftime('%Y-%m-%d %H:%M:%S'))
- fdt = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S')
- if fdt > build_stamp:
- pkg_diffs['pkg'].update(
- {str(re.sub('\.list', '', file)): str(fdt)})
-
- if len(pkg_diffs['pkg']) != 0:
- return pkg_diffs
- else:
- return None
-
-
-if __name__ == '__main__':
- built_date = _get_sys_build_version()
- if not built_date:
- sys.exit(1)
- pkgs = _check_pkgs(built_date)
- if pkgs:
- print (
- "The following packages don\'t fit the image creation time\nbuild time:\t" + pkgs['buildtime'])
- for k, v in pkgs['pkg'].items():
- print ("installed: " + v + '\t' + k)
diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py
index 5bbc2e1f1..7962e1e7b 100755
--- a/src/op_mode/show_version.py
+++ b/src/op_mode/show_version.py
@@ -32,12 +32,12 @@ parser.add_argument("-j", "--json", action="store_true", help="Produce JSON outp
version_output_tmpl = """
Version: VyOS {{version}}
-Release Train: {{release_train}}
+Release train: {{release_train}}
Built by: {{built_by}}
Built on: {{built_on}}
Build UUID: {{build_uuid}}
-Build Commit ID: {{build_git}}
+Build commit ID: {{build_git}}
Architecture: {{system_arch}}
Boot via: {{boot_via}}
diff --git a/src/op_mode/show_wwan.py b/src/op_mode/show_wwan.py
index 249dda2a5..529b5bd0f 100755
--- a/src/op_mode/show_wwan.py
+++ b/src/op_mode/show_wwan.py
@@ -34,13 +34,17 @@ required = parser.add_argument_group('Required arguments')
required.add_argument("--interface", help="WWAN interface name, e.g. wwan0", required=True)
def qmi_cmd(device, command, silent=False):
- tmp = cmd(f'qmicli --device={device} --device-open-proxy {command}')
- tmp = tmp.replace(f'[{cdc}] ', '')
- if not silent:
- # skip first line as this only holds the info headline
- for line in tmp.splitlines()[1:]:
- print(line.lstrip())
- return tmp
+ try:
+ tmp = cmd(f'qmicli --device={device} --device-open-proxy {command}')
+ tmp = tmp.replace(f'[{cdc}] ', '')
+ if not silent:
+ # skip first line as this only holds the info headline
+ for line in tmp.splitlines()[1:]:
+ print(line.lstrip())
+ return tmp
+ except:
+ print('Command not supported by Modem')
+ exit(1)
if __name__ == '__main__':
args = parser.parse_args()
diff --git a/src/services/api/graphql/README.graphql b/src/services/api/graphql/README.graphql
new file mode 100644
index 000000000..c91b70782
--- /dev/null
+++ b/src/services/api/graphql/README.graphql
@@ -0,0 +1,140 @@
+
+Example using GraphQL mutations to configure a DHCP server:
+
+This assumes that the http-api is running:
+
+'set service https api'
+
+One can configure an address on an interface, and configure the DHCP server
+to run with that address as default router by requesting these 'mutations'
+in the GraphQL playground:
+
+mutation {
+ createInterfaceEthernet (data: {interface: "eth1",
+ address: "192.168.0.1/24",
+ description: "BOB"}) {
+ success
+ errors
+ data {
+ address
+ }
+ }
+}
+
+mutation {
+ createDhcpServer(data: {sharedNetworkName: "BOB",
+ subnet: "192.168.0.0/24",
+ defaultRouter: "192.168.0.1",
+ nameServer: "192.168.0.1",
+ domainName: "vyos.net",
+ lease: 86400,
+ range: 0,
+ start: "192.168.0.9",
+ stop: "192.168.0.254",
+ dnsForwardingAllowFrom: "192.168.0.0/24",
+ dnsForwardingCacheSize: 0,
+ dnsForwardingListenAddress: "192.168.0.1"}) {
+ success
+ errors
+ data {
+ defaultRouter
+ }
+ }
+}
+
+mutation {
+ saveConfigFile(data: {fileName: "/config/config.boot"}) {
+ success
+ errors
+ data {
+ fileName
+ }
+ }
+}
+
+N.B. fileName can be empty (fileName: "") or data can be empty (data: {}) to save to
+/config/config.boot; to save to an alternative path, specify fileName.
+
+mutation {
+ loadConfigFile(data: {fileName: "/home/vyos/config.boot"}) {
+ success
+ errors
+ data {
+ fileName
+ }
+ }
+}
+
+
+The GraphQL playground will be found at:
+
+https://{{ host_address }}/graphql
+
+An equivalent curl command to the first example above would be:
+
+curl -k 'https://192.168.100.168/graphql' -H 'Content-Type: application/json' --data-binary '{"query": "mutation {createInterfaceEthernet (data: {interface: \"eth1\", address: \"192.168.0.1/24\", description: \"BOB\"}) {success errors data {address}}}"}'
+
+Note that the 'mutation' term is prefaced by 'query' in the curl command.
+
+What's here:
+
+services
+├── api
+│   └── graphql
+│   ├── graphql
+│   │   ├── directives.py
+│   │   ├── __init__.py
+│   │   ├── mutations.py
+│   │   └── schema
+│   │   ├── dhcp_server.graphql
+│   │   ├── interface_ethernet.graphql
+│   │   └── schema.graphql
+│   ├── recipes
+│   │   ├── dhcp_server.py
+│   │   ├── __init__.py
+│   │   ├── interface_ethernet.py
+│   │   ├── recipe.py
+│   │   └── templates
+│   │   ├── dhcp_server.tmpl
+│   │   └── interface_ethernet.tmpl
+│   └── state.py
+├── vyos-configd
+├── vyos-hostsd
+└── vyos-http-api-server
+
+The GraphQL library that we are using, Ariadne, advertises itself as a
+'schema-first' implementation: define the schema; define resolvers
+(handlers) for declared Query and Mutation types (Subscription types are not
+currently used).
+
+In the current approach to a high-level API, we consider the
+Jinja2-templated collection of configuration mode 'set'/'delete' commands as
+the Ur-data; the GraphQL schema is produced from those files, located in
+'api/graphql/recipes/templates'.
+
+Resolvers for the schema Mutation fields are dynamically generated using a
+'directive' added to the respective schema field. The directive,
+'@generate', is handled by the class 'DataDirective' in
+'api/graphql/graphql/directives.py', which calls the 'make_resolver' function in
+'api/graphql/graphql/mutations.py'; the produced resolver calls the appropriate
+wrapper in 'api/graphql/recipes', with base class doing the (overridable)
+configuration steps of calling all defined 'set'/'delete' commands.
+
+Integrating the above with vyos-http-api-server is ~10 lines of code.
+
+What needs to be done:
+
+• automate generation of schema and wrappers from templated configuration
+commands
+
+• investigate whether the subclassing provided by the named wrappers in
+'api/graphql/recipes' is sufficient for use cases which need to modify data
+
+• encapsulate the manipulation of 'canonical names' which transforms the
+prefixed camel-case schema names to various snake-case file/function names
+
+• consider mechanism for migration of templates: offline vs. on-the-fly
+
+• define the naming convention for those schema fields that refer to
+configuration mode parameters: e.g. how much of the path is needed as prefix
+to uniquely define the term
diff --git a/src/services/api/graphql/bindings.py b/src/services/api/graphql/bindings.py
new file mode 100644
index 000000000..c123f68d8
--- /dev/null
+++ b/src/services/api/graphql/bindings.py
@@ -0,0 +1,14 @@
+import vyos.defaults
+from . graphql.mutations import mutation
+from . graphql.directives import DataDirective, ConfigFileDirective
+
+from ariadne import make_executable_schema, load_schema_from_path, snake_case_fallback_resolvers
+
+def generate_schema():
+ api_schema_dir = vyos.defaults.directories['api_schema']
+
+ type_defs = load_schema_from_path(api_schema_dir)
+
+ schema = make_executable_schema(type_defs, mutation, snake_case_fallback_resolvers, directives={"generate": DataDirective, "configfile": ConfigFileDirective})
+
+ return schema
diff --git a/src/services/api/graphql/graphql/__init__.py b/src/services/api/graphql/graphql/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/services/api/graphql/graphql/__init__.py
diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py
new file mode 100644
index 000000000..85d514de4
--- /dev/null
+++ b/src/services/api/graphql/graphql/directives.py
@@ -0,0 +1,32 @@
+from ariadne import SchemaDirectiveVisitor, ObjectType
+from . mutations import make_resolver, make_config_file_resolver
+
+class DataDirective(SchemaDirectiveVisitor):
+ """
+ Class providing implementation of 'generate' directive in schema.
+
+ """
+ def visit_field_definition(self, field, object_type):
+ name = f'{field.type}'
+ # field.type contains the return value of the mutation; trim value
+ # to produce canonical name
+ name = name.replace('Result', '', 1)
+
+ func = make_resolver(name)
+ field.resolve = func
+ return field
+
+class ConfigFileDirective(SchemaDirectiveVisitor):
+ """
+ Class providing implementation of 'configfile' directive in schema.
+
+ """
+ def visit_field_definition(self, field, object_type):
+ name = f'{field.type}'
+ # field.type contains the return value of the mutation; trim value
+ # to produce canonical name
+ name = name.replace('Result', '', 1)
+
+ func = make_config_file_resolver(name)
+ field.resolve = func
+ return field
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
new file mode 100644
index 000000000..2eb0a0b4a
--- /dev/null
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -0,0 +1,109 @@
+
+from importlib import import_module
+from typing import Any, Dict
+from ariadne import ObjectType, convert_kwargs_to_snake_case, convert_camel_case_to_snake
+from graphql import GraphQLResolveInfo
+from makefun import with_signature
+
+from .. import state
+
+mutation = ObjectType("Mutation")
+
+def make_resolver(mutation_name):
+ """Dynamically generate a resolver for the mutation named in the
+ schema by 'mutation_name'.
+
+ Dynamic generation is provided using the package 'makefun' (via the
+ decorator 'with_signature'), which provides signature-preserving
+ function wrappers; it provides several improvements over, say,
+ functools.wraps.
+
+ :raise Exception:
+ encapsulating ConfigErrors, or internal errors
+ """
+ class_name = mutation_name.replace('create', '', 1).replace('delete', '', 1)
+ func_base_name = convert_camel_case_to_snake(class_name)
+ resolver_name = f'resolve_create_{func_base_name}'
+ func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Dict)'
+
+ @mutation.field(mutation_name)
+ @convert_kwargs_to_snake_case
+ @with_signature(func_sig, func_name=resolver_name)
+ async def func_impl(*args, **kwargs):
+ try:
+ if 'data' not in kwargs:
+ return {
+ "success": False,
+ "errors": ['missing data']
+ }
+
+ data = kwargs['data']
+ session = state.settings['app'].state.vyos_session
+
+ mod = import_module(f'api.graphql.recipes.{func_base_name}')
+ klass = getattr(mod, class_name)
+ k = klass(session, data)
+ k.configure()
+
+ return {
+ "success": True,
+ "data": data
+ }
+ except Exception as error:
+ return {
+ "success": False,
+ "errors": [str(error)]
+ }
+
+ return func_impl
+
+def make_config_file_resolver(mutation_name):
+ op = ''
+ if 'save' in mutation_name:
+ op = 'save'
+ elif 'load' in mutation_name:
+ op = 'load'
+
+ class_name = mutation_name.replace('save', '', 1).replace('load', '', 1)
+ func_base_name = convert_camel_case_to_snake(class_name)
+ resolver_name = f'resolve_{func_base_name}'
+ func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Dict)'
+
+ @mutation.field(mutation_name)
+ @convert_kwargs_to_snake_case
+ @with_signature(func_sig, func_name=resolver_name)
+ async def func_impl(*args, **kwargs):
+ try:
+ if 'data' not in kwargs:
+ return {
+ "success": False,
+ "errors": ['missing data']
+ }
+
+ data = kwargs['data']
+ session = state.settings['app'].state.vyos_session
+
+ mod = import_module(f'api.graphql.recipes.{func_base_name}')
+ klass = getattr(mod, class_name)
+ k = klass(session, data)
+ if op == 'save':
+ k.save()
+ elif op == 'load':
+ k.load()
+ else:
+ return {
+ "success": False,
+ "errors": ["Input must be saveConfigFile | loadConfigFile"]
+ }
+
+ return {
+ "success": True,
+ "data": data
+ }
+ except Exception as error:
+ return {
+ "success": False,
+ "errors": [str(error)]
+ }
+
+ return func_impl
diff --git a/src/services/api/graphql/graphql/schema/config_file.graphql b/src/services/api/graphql/graphql/schema/config_file.graphql
new file mode 100644
index 000000000..3096cf743
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/config_file.graphql
@@ -0,0 +1,27 @@
+input saveConfigFileInput {
+ fileName: String
+}
+
+type saveConfigFile {
+ fileName: String
+}
+
+type saveConfigFileResult {
+ data: saveConfigFile
+ success: Boolean!
+ errors: [String]
+}
+
+input loadConfigFileInput {
+ fileName: String!
+}
+
+type loadConfigFile {
+ fileName: String!
+}
+
+type loadConfigFileResult {
+ data: loadConfigFile
+ success: Boolean!
+ errors: [String]
+}
diff --git a/src/services/api/graphql/graphql/schema/dhcp_server.graphql b/src/services/api/graphql/graphql/schema/dhcp_server.graphql
new file mode 100644
index 000000000..9f741a0a5
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/dhcp_server.graphql
@@ -0,0 +1,35 @@
+input dhcpServerConfigInput {
+ sharedNetworkName: String
+ subnet: String
+ defaultRouter: String
+ nameServer: String
+ domainName: String
+ lease: Int
+ range: Int
+ start: String
+ stop: String
+ dnsForwardingAllowFrom: String
+ dnsForwardingCacheSize: Int
+ dnsForwardingListenAddress: String
+}
+
+type dhcpServerConfig {
+ sharedNetworkName: String
+ subnet: String
+ defaultRouter: String
+ nameServer: String
+ domainName: String
+ lease: Int
+ range: Int
+ start: String
+ stop: String
+ dnsForwardingAllowFrom: String
+ dnsForwardingCacheSize: Int
+ dnsForwardingListenAddress: String
+}
+
+type createDhcpServerResult {
+ data: dhcpServerConfig
+ success: Boolean!
+ errors: [String]
+}
diff --git a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql b/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
new file mode 100644
index 000000000..fdcf97bad
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
@@ -0,0 +1,18 @@
+input interfaceEthernetConfigInput {
+ interface: String
+ address: String
+ replace: Boolean = true
+ description: String
+}
+
+type interfaceEthernetConfig {
+ interface: String
+ address: String
+ description: String
+}
+
+type createInterfaceEthernetResult {
+ data: interfaceEthernetConfig
+ success: Boolean!
+ errors: [String]
+}
diff --git a/src/services/api/graphql/graphql/schema/schema.graphql b/src/services/api/graphql/graphql/schema/schema.graphql
new file mode 100644
index 000000000..70fe0d726
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/schema.graphql
@@ -0,0 +1,18 @@
+schema {
+ query: Query
+ mutation: Mutation
+}
+
+type Query {
+ _dummy: String
+}
+
+directive @generate on FIELD_DEFINITION
+directive @configfile on FIELD_DEFINITION
+
+type Mutation {
+ createDhcpServer(data: dhcpServerConfigInput) : createDhcpServerResult @generate
+ createInterfaceEthernet(data: interfaceEthernetConfigInput) : createInterfaceEthernetResult @generate
+ saveConfigFile(data: saveConfigFileInput) : saveConfigFileResult @configfile
+ loadConfigFile(data: loadConfigFileInput) : loadConfigFileResult @configfile
+}
diff --git a/src/services/api/graphql/recipes/__init__.py b/src/services/api/graphql/recipes/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/services/api/graphql/recipes/__init__.py
diff --git a/src/services/api/graphql/recipes/config_file.py b/src/services/api/graphql/recipes/config_file.py
new file mode 100644
index 000000000..850e5326e
--- /dev/null
+++ b/src/services/api/graphql/recipes/config_file.py
@@ -0,0 +1,16 @@
+
+from . recipe import Recipe
+
+class ConfigFile(Recipe):
+ def __init__(self, session, command_file):
+ super().__init__(session, command_file)
+
+ # Define any custom processing of parameters here by overriding
+ # save/load:
+ #
+ # def save(self):
+ # self.data = transform_data(self.data)
+ # super().save()
+ # def load(self):
+ # self.data = transform_data(self.data)
+ # super().load()
diff --git a/src/services/api/graphql/recipes/dhcp_server.py b/src/services/api/graphql/recipes/dhcp_server.py
new file mode 100644
index 000000000..3edb3028e
--- /dev/null
+++ b/src/services/api/graphql/recipes/dhcp_server.py
@@ -0,0 +1,13 @@
+
+from . recipe import Recipe
+
+class DhcpServer(Recipe):
+ def __init__(self, session, command_file):
+ super().__init__(session, command_file)
+
+ # Define any custom processing of parameters here by overriding
+ # configure:
+ #
+ # def configure(self):
+ # self.data = transform_data(self.data)
+ # super().configure()
diff --git a/src/services/api/graphql/recipes/interface_ethernet.py b/src/services/api/graphql/recipes/interface_ethernet.py
new file mode 100644
index 000000000..f88f5924f
--- /dev/null
+++ b/src/services/api/graphql/recipes/interface_ethernet.py
@@ -0,0 +1,13 @@
+
+from . recipe import Recipe
+
+class InterfaceEthernet(Recipe):
+ def __init__(self, session, command_file):
+ super().__init__(session, command_file)
+
+ # Define any custom processing of parameters here by overriding
+ # configure:
+ #
+ # def configure(self):
+ # self.data = transform_data(self.data)
+ # super().configure()
diff --git a/src/services/api/graphql/recipes/recipe.py b/src/services/api/graphql/recipes/recipe.py
new file mode 100644
index 000000000..91d8bd67a
--- /dev/null
+++ b/src/services/api/graphql/recipes/recipe.py
@@ -0,0 +1,68 @@
+from ariadne import convert_camel_case_to_snake
+import vyos.defaults
+from vyos.template import render
+
+class Recipe(object):
+ def __init__(self, session, data):
+ self._session = session
+ self.data = data
+ self._name = convert_camel_case_to_snake(type(self).__name__)
+
+ @property
+ def data(self):
+ return self.__data
+
+ @data.setter
+ def data(self, data):
+ if isinstance(data, dict):
+ self.__data = data
+ else:
+ raise ValueError("data must be of type dict")
+
+ def configure(self):
+ session = self._session
+ data = self.data
+ func_base_name = self._name
+
+ tmpl_file = f'{func_base_name}.tmpl'
+ cmd_file = f'/tmp/{func_base_name}.cmds'
+ tmpl_dir = vyos.defaults.directories['api_templates']
+
+ try:
+ render(cmd_file, tmpl_file, data, location=tmpl_dir)
+ commands = []
+ with open(cmd_file) as f:
+ lines = f.readlines()
+ for line in lines:
+ commands.append(line.split())
+ for cmd in commands:
+ if cmd[0] == 'set':
+ session.set(cmd[1:])
+ elif cmd[0] == 'delete':
+ session.delete(cmd[1:])
+ else:
+ raise ValueError('Operation must be "set" or "delete"')
+ session.commit()
+ except Exception as error:
+ raise error
+
+ def save(self):
+ session = self._session
+ data = self.data
+ if 'file_name' not in data or not data['file_name']:
+ data['file_name'] = '/config/config.boot'
+
+ try:
+ session.save_config(data['file_name'])
+ except Exception as error:
+ raise error
+
+ def load(self):
+ session = self._session
+ data = self.data
+
+ try:
+ session.load_config(data['file_name'])
+ session.commit()
+ except Exception as error:
+ raise error
diff --git a/src/services/api/graphql/recipes/templates/dhcp_server.tmpl b/src/services/api/graphql/recipes/templates/dhcp_server.tmpl
new file mode 100644
index 000000000..70de43183
--- /dev/null
+++ b/src/services/api/graphql/recipes/templates/dhcp_server.tmpl
@@ -0,0 +1,9 @@
+set service dhcp-server shared-network-name {{ shared_network_name }} subnet {{ subnet }} default-router {{ default_router }}
+set service dhcp-server shared-network-name {{ shared_network_name }} subnet {{ subnet }} name-server {{ name_server }}
+set service dhcp-server shared-network-name {{ shared_network_name }} subnet {{ subnet }} domain-name {{ domain_name }}
+set service dhcp-server shared-network-name {{ shared_network_name }} subnet {{ subnet }} lease {{ lease }}
+set service dhcp-server shared-network-name {{ shared_network_name }} subnet {{ subnet }} range {{ range }} start {{ start }}
+set service dhcp-server shared-network-name {{ shared_network_name }} subnet {{ subnet }} range {{ range }} stop {{ stop }}
+set service dns forwarding allow-from {{ dns_forwarding_allow_from }}
+set service dns forwarding cache-size {{ dns_forwarding_cache_size }}
+set service dns forwarding listen-address {{ dns_forwarding_listen_address }}
diff --git a/src/services/api/graphql/recipes/templates/interface_ethernet.tmpl b/src/services/api/graphql/recipes/templates/interface_ethernet.tmpl
new file mode 100644
index 000000000..d9d7ed691
--- /dev/null
+++ b/src/services/api/graphql/recipes/templates/interface_ethernet.tmpl
@@ -0,0 +1,5 @@
+{% if replace %}
+delete interfaces ethernet {{ interface }} address
+{% endif %}
+set interfaces ethernet {{ interface }} address {{ address }}
+set interfaces ethernet {{ interface }} description {{ description }}
diff --git a/src/services/api/graphql/state.py b/src/services/api/graphql/state.py
new file mode 100644
index 000000000..63db9f4ef
--- /dev/null
+++ b/src/services/api/graphql/state.py
@@ -0,0 +1,4 @@
+
+def init():
+ global settings
+ settings = {}
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 6f770b696..670b6e66a 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -133,8 +133,7 @@ def explicit_print(path, mode, msg):
logger.critical("error explicit_print")
def run_script(script, config, args) -> int:
- if args:
- script.argv = args
+ script.argv = args
config.set_level([])
try:
c = script.get_config(config)
@@ -208,7 +207,7 @@ def process_node_data(config, data) -> int:
return R_ERROR_DAEMON
script_name = None
- args = None
+ args = []
res = re.match(r'^(VYOS_TAGNODE_VALUE=[^/]+)?.*\/([^/]+).py(.*)', data)
if res.group(1):
@@ -221,7 +220,7 @@ def process_node_data(config, data) -> int:
return R_ERROR_DAEMON
if res.group(3):
args = res.group(3).split()
- args.insert(0, f'{script_name}.py')
+ args.insert(0, f'{script_name}.py')
if script_name not in include_set:
return R_PASS
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 703628558..aa7ac6708 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -1,6 +1,6 @@
-#!/usr/bin/env python3
+#!/usr/share/vyos-http-api-tools/bin/python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -19,25 +19,43 @@
import os
import sys
import grp
+import copy
import json
+import logging
import traceback
import threading
-import signal
+from typing import List, Union, Callable, Dict
-import vyos.config
-
-from flask import Flask, request
-from waitress import serve
+import uvicorn
+from fastapi import FastAPI, Depends, Request, Response, HTTPException
+from fastapi.responses import HTMLResponse
+from fastapi.exceptions import RequestValidationError
+from fastapi.routing import APIRoute
+from pydantic import BaseModel, StrictStr, validator
+from starlette.datastructures import FormData
+from starlette.formparsers import FormParser, MultiPartParser
+from multipart.multipart import parse_options_header
-from functools import wraps
+from ariadne.asgi import GraphQL
+import vyos.config
from vyos.configsession import ConfigSession, ConfigSessionError
+import api.graphql.state
DEFAULT_CONFIG_FILE = '/etc/vyos/http-api.conf'
CFG_GROUP = 'vyattacfg'
-app = Flask(__name__)
+debug = True
+
+logger = logging.getLogger(__name__)
+logs_handler = logging.StreamHandler()
+logger.addHandler(logs_handler)
+
+if debug:
+ logger.setLevel(logging.DEBUG)
+else:
+ logger.setLevel(logging.INFO)
# Giant lock!
lock = threading.Lock()
@@ -48,63 +66,347 @@ def load_server_config():
return config
def check_auth(key_list, key):
- id = None
+ key_id = None
for k in key_list:
if k['key'] == key:
- id = k['id']
- return id
+ key_id = k['id']
+ return key_id
def error(code, msg):
resp = {"success": False, "error": msg, "data": None}
- return json.dumps(resp), code
+ resp = json.dumps(resp)
+ return HTMLResponse(resp, status_code=code)
def success(data):
resp = {"success": True, "data": data, "error": None}
- return json.dumps(resp)
-
-def get_command(f):
- @wraps(f)
- def decorated_function(*args, **kwargs):
- cmd = request.form.get("data")
- if not cmd:
- return error(400, "Non-empty data field is required")
- try:
- cmd = json.loads(cmd)
- except Exception as e:
- return error(400, "Failed to parse JSON: {0}".format(e))
- return f(cmd, *args, **kwargs)
-
- return decorated_function
-
-def auth_required(f):
- @wraps(f)
- def decorated_function(*args, **kwargs):
- key = request.form.get("key")
- api_keys = app.config['vyos_keys']
- id = check_auth(api_keys, key)
- if not id:
- return error(401, "Valid API key is required")
- return f(*args, **kwargs)
-
- return decorated_function
-
-@app.route('/configure', methods=['POST'])
-@get_command
-@auth_required
-def configure_op(commands):
- session = app.config['vyos_session']
+ resp = json.dumps(resp)
+ return HTMLResponse(resp)
+
+# Pydantic models for validation
+# Pydantic will cast when possible, so use StrictStr
+# validators added as needed for additional constraints
+# schema_extra adds anotations to OpenAPI, to add examples
+
+class ApiModel(BaseModel):
+ key: StrictStr
+
+class BaseConfigureModel(BaseModel):
+ op: StrictStr
+ path: List[StrictStr]
+ value: StrictStr = None
+
+ @validator("path", pre=True, always=True)
+ def check_non_empty(cls, path):
+ assert len(path) > 0
+ return path
+
+class ConfigureModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+ value: StrictStr = None
+
+ @validator("path", pre=True, always=True)
+ def check_non_empty(cls, path):
+ assert len(path) > 0
+ return path
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "set | delete | comment",
+ "path": ['config', 'mode', 'path'],
+ }
+ }
+
+class ConfigureListModel(ApiModel):
+ commands: List[BaseConfigureModel]
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "commands": "list of commands",
+ }
+ }
+
+class RetrieveModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+ configFormat: StrictStr = None
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "returnValue | returnValues | exists | showConfig",
+ "path": ['config', 'mode', 'path'],
+ "configFormat": "json (default) | json_ast | raw",
+
+ }
+ }
+
+class ConfigFileModel(ApiModel):
+ op: StrictStr
+ file: StrictStr = None
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "save | load",
+ "file": "filename",
+ }
+ }
+
+class ImageModel(ApiModel):
+ op: StrictStr
+ url: StrictStr = None
+ name: StrictStr = None
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "add | delete",
+ "url": "imagelocation",
+ "name": "imagename",
+ }
+ }
+
+class GenerateModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "generate",
+ "path": ["op", "mode", "path"],
+ }
+ }
+
+class ShowModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "show",
+ "path": ["op", "mode", "path"],
+ }
+ }
+
+class Success(BaseModel):
+ success: bool
+ data: Union[str, bool, Dict]
+ error: str
+
+class Error(BaseModel):
+ success: bool = False
+ data: Union[str, bool, Dict]
+ error: str
+
+responses = {
+ 200: {'model': Success},
+ 400: {'model': Error},
+ 422: {'model': Error, 'description': 'Validation Error'},
+ 500: {'model': Error}
+}
+
+def auth_required(data: ApiModel):
+ key = data.key
+ api_keys = app.state.vyos_keys
+ key_id = check_auth(api_keys, key)
+ if not key_id:
+ raise HTTPException(status_code=401, detail="Valid API key is required")
+ app.state.vyos_id = key_id
+
+# override Request and APIRoute classes in order to convert form request to json;
+# do all explicit validation here, for backwards compatability of error messages;
+# the explicit validation may be dropped, if desired, in favor of native
+# validation by FastAPI/Pydantic, as is used for application/json requests
+class MultipartRequest(Request):
+ ERR_MISSING_KEY = False
+ ERR_MISSING_DATA = False
+ ERR_NOT_JSON = False
+ ERR_NOT_DICT = False
+ ERR_NO_OP = False
+ ERR_NO_PATH = False
+ ERR_EMPTY_PATH = False
+ ERR_PATH_NOT_LIST = False
+ ERR_VALUE_NOT_STRING = False
+ ERR_PATH_NOT_LIST_OF_STR = False
+ offending_command = {}
+ exception = None
+
+ @property
+ def orig_headers(self):
+ self._orig_headers = super().headers
+ return self._orig_headers
+
+ @property
+ def headers(self):
+ self._headers = super().headers.mutablecopy()
+ self._headers['content-type'] = 'application/json'
+ return self._headers
+
+ async def form(self) -> FormData:
+ if not hasattr(self, "_form"):
+ assert (
+ parse_options_header is not None
+ ), "The `python-multipart` library must be installed to use form parsing."
+ content_type_header = self.orig_headers.get("Content-Type")
+ content_type, options = parse_options_header(content_type_header)
+ if content_type == b"multipart/form-data":
+ multipart_parser = MultiPartParser(self.orig_headers, self.stream())
+ self._form = await multipart_parser.parse()
+ elif content_type == b"application/x-www-form-urlencoded":
+ form_parser = FormParser(self.orig_headers, self.stream())
+ self._form = await form_parser.parse()
+ else:
+ self._form = FormData()
+ return self._form
+
+ async def body(self) -> bytes:
+ if not hasattr(self, "_body"):
+ forms = {}
+ merge = {}
+ body = await super().body()
+ self._body = body
+
+ form_data = await self.form()
+ if form_data:
+ logger.debug("processing form data")
+ for k, v in form_data.multi_items():
+ forms[k] = v
+
+ if 'data' not in forms:
+ self.ERR_MISSING_DATA = True
+ else:
+ try:
+ tmp = json.loads(forms['data'])
+ except json.JSONDecodeError as e:
+ self.ERR_NOT_JSON = True
+ self.exception = e
+ tmp = {}
+ if isinstance(tmp, list):
+ merge['commands'] = tmp
+ else:
+ merge = tmp
+
+ if 'commands' in merge:
+ cmds = merge['commands']
+ else:
+ cmds = copy.deepcopy(merge)
+ cmds = [cmds]
+
+ for c in cmds:
+ if not isinstance(c, dict):
+ self.ERR_NOT_DICT = True
+ self.offending_command = c
+ elif 'op' not in c:
+ self.ERR_NO_OP = True
+ self.offending_command = c
+ elif 'path' not in c:
+ self.ERR_NO_PATH = True
+ self.offending_command = c
+ elif not c['path']:
+ self.ERR_EMPTY_PATH = True
+ self.offending_command = c
+ elif not isinstance(c['path'], list):
+ self.ERR_PATH_NOT_LIST = True
+ self.offending_command = c
+ elif not all(isinstance(el, str) for el in c['path']):
+ self.ERR_PATH_NOT_LIST_OF_STR = True
+ self.offending_command = c
+ elif 'value' in c and not isinstance(c['value'], str):
+ self.ERR_VALUE_NOT_STRING = True
+ self.offending_command = c
+
+ if 'key' not in forms and 'key' not in merge:
+ self.ERR_MISSING_KEY = True
+ if 'key' in forms and 'key' not in merge:
+ merge['key'] = forms['key']
+
+ new_body = json.dumps(merge)
+ new_body = new_body.encode()
+ self._body = new_body
+
+ return self._body
+
+class MultipartRoute(APIRoute):
+ def get_route_handler(self) -> Callable:
+ original_route_handler = super().get_route_handler()
+
+ async def custom_route_handler(request: Request) -> Response:
+ request = MultipartRequest(request.scope, request.receive)
+ endpoint = request.url.path
+ try:
+ response: Response = await original_route_handler(request)
+ except HTTPException as e:
+ return error(e.status_code, e.detail)
+ except Exception as e:
+ if request.ERR_MISSING_KEY:
+ return error(422, "Valid API key is required")
+ if request.ERR_MISSING_DATA:
+ return error(422, "Non-empty data field is required")
+ if request.ERR_NOT_JSON:
+ return error(400, "Failed to parse JSON: {0}".format(request.exception))
+ if endpoint == '/configure':
+ if request.ERR_NOT_DICT:
+ return error(400, "Malformed command \"{0}\": any command must be a dict".format(json.dumps(request.offending_command)))
+ if request.ERR_NO_OP:
+ return error(400, "Malformed command \"{0}\": missing \"op\" field".format(json.dumps(request.offending_command)))
+ if request.ERR_NO_PATH:
+ return error(400, "Malformed command \"{0}\": missing \"path\" field".format(json.dumps(request.offending_command)))
+ if request.ERR_EMPTY_PATH:
+ return error(400, "Malformed command \"{0}\": empty path".format(json.dumps(request.offending_command)))
+ if request.ERR_PATH_NOT_LIST:
+ return error(400, "Malformed command \"{0}\": \"path\" field must be a list".format(json.dumps(request.offending_command)))
+ if request.ERR_VALUE_NOT_STRING:
+ return error(400, "Malformed command \"{0}\": \"value\" field must be a string".format(json.dumps(request.offending_command)))
+ if request.ERR_PATH_NOT_LIST_OF_STR:
+ return error(400, "Malformed command \"{0}\": \"path\" field must be a list of strings".format(json.dumps(request.offending_command)))
+ if endpoint in ('/retrieve','/generate','/show'):
+ if request.ERR_NO_OP or request.ERR_NO_PATH:
+ return error(400, "Missing required field. \"op\" and \"path\" fields are required")
+ if endpoint in ('/config-file', '/image'):
+ if request.ERR_NO_OP:
+ return error(400, "Missing required field \"op\"")
+
+ raise e
+
+ return response
+
+ return custom_route_handler
+
+app = FastAPI(debug=True,
+ title="VyOS API",
+ version="0.1.0",
+ responses={**responses},
+ dependencies=[Depends(auth_required)])
+
+app.router.route_class = MultipartRoute
+
+@app.exception_handler(RequestValidationError)
+async def validation_exception_handler(request, exc):
+ return error(400, str(exc.errors()[0]))
+
+@app.post('/configure')
+def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
+ session = app.state.vyos_session
env = session.get_session_env()
config = vyos.config.Config(session_env=env)
- strict_field = request.form.get("strict")
- if strict_field == "true":
- strict = True
- else:
- strict = False
-
# Allow users to pass just one command
- if not isinstance(commands, list):
- commands = [commands]
+ if not isinstance(data, ConfigureListModel):
+ data = [data]
+ else:
+ data = data.commands
# We don't want multiple people/apps to be able to commit at once,
# or modify the shared session while someone else is doing the same,
@@ -114,53 +416,25 @@ def configure_op(commands):
status = 200
error_msg = None
try:
- for c in commands:
- # What we've got may not even be a dict
- if not isinstance(c, dict):
- raise ConfigSessionError("Malformed command \"{0}\": any command must be a dict".format(json.dumps(c)))
-
- # Missing op or path is a show stopper
- if not ('op' in c):
- raise ConfigSessionError("Malformed command \"{0}\": missing \"op\" field".format(json.dumps(c)))
- if not ('path' in c):
- raise ConfigSessionError("Malformed command \"{0}\": missing \"path\" field".format(json.dumps(c)))
-
- # Missing value is fine, substitute for empty string
- if 'value' in c:
- value = c['value']
- else:
- value = ""
-
- op = c['op']
- path = c['path']
-
- if not path:
- raise ConfigSessionError("Malformed command \"{0}\": empty path".format(json.dumps(c)))
-
- # Type checking
- if not isinstance(path, list):
- raise ConfigSessionError("Malformed command \"{0}\": \"path\" field must be a list".format(json.dumps(c)))
-
- if not isinstance(value, str):
- raise ConfigSessionError("Malformed command \"{0}\": \"value\" field must be a string".format(json.dumps(c)))
+ for c in data:
+ op = c.op
+ path = c.path
- # Account for the case when value field is present and set to null
- if not value:
+ if c.value:
+ value = c.value
+ else:
value = ""
- # For vyos.configsessios calls that have no separate value arguments,
+ # For vyos.configsession calls that have no separate value arguments,
# and for type checking too
- try:
- cfg_path = " ".join(path + [value]).strip()
- except TypeError:
- raise ConfigSessionError("Malformed command \"{0}\": \"path\" field must be a list of strings".format(json.dumps(c)))
+ cfg_path = " ".join(path + [value]).strip()
if op == 'set':
# XXX: it would be nice to do a strict check for "path already exists",
# but there's probably no way to do that
session.set(path, value=value)
elif op == 'delete':
- if strict and not config.exists(cfg_path):
+ if app.state.vyos_strict and not config.exists(cfg_path):
raise ConfigSessionError("Cannot delete [{0}]: path/value does not exist".format(cfg_path))
session.delete(path, value=value)
elif op == 'comment':
@@ -169,16 +443,16 @@ def configure_op(commands):
raise ConfigSessionError("\"{0}\" is not a valid operation".format(op))
# end for
session.commit()
- print("Configuration modified via HTTP API using key \"{0}\"".format(id))
+ logger.info(f"Configuration modified via HTTP API using key '{app.state.vyos_id}'")
except ConfigSessionError as e:
session.discard()
status = 400
- if app.config['vyos_debug']:
- print(traceback.format_exc(), file=sys.stderr)
+ if app.state.vyos_debug:
+ logger.critical(f"ConfigSessionError:\n {traceback.format_exc()}")
error_msg = str(e)
except Exception as e:
session.discard()
- print(traceback.format_exc(), file=sys.stderr)
+ logger.critical(traceback.format_exc())
status = 500
# Don't give the details away to the outer world
@@ -188,22 +462,17 @@ def configure_op(commands):
if status != 200:
return error(status, error_msg)
- else:
- return success(None)
-@app.route('/retrieve', methods=['POST'])
-@get_command
-@auth_required
-def retrieve_op(command):
- session = app.config['vyos_session']
+ return success(None)
+
+@app.post("/retrieve")
+def retrieve_op(data: RetrieveModel):
+ session = app.state.vyos_session
env = session.get_session_env()
config = vyos.config.Config(session_env=env)
- try:
- op = command['op']
- path = " ".join(command['path'])
- except KeyError:
- return error(400, "Missing required field. \"op\" and \"path\" fields are required")
+ op = data.op
+ path = " ".join(data.path)
try:
if op == 'returnValue':
@@ -214,10 +483,10 @@ def retrieve_op(command):
res = config.exists(path)
elif op == 'showConfig':
config_format = 'json'
- if 'configFormat' in command:
- config_format = command['configFormat']
+ if data.configFormat:
+ config_format = data.configFormat
- res = session.show_config(path=command['path'])
+ res = session.show_config(path=data.path)
if config_format == 'json':
config_tree = vyos.configtree.ConfigTree(res)
res = json.loads(config_tree.to_json())
@@ -233,33 +502,28 @@ def retrieve_op(command):
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
- print(traceback.format_exc(), file=sys.stderr)
+ logger.critical(traceback.format_exc())
return error(500, "An internal error occured. Check the logs for details.")
return success(res)
-@app.route('/config-file', methods=['POST'])
-@get_command
-@auth_required
-def config_file_op(command):
- session = app.config['vyos_session']
+@app.post('/config-file')
+def config_file_op(data: ConfigFileModel):
+ session = app.state.vyos_session
- try:
- op = command['op']
- except KeyError:
- return error(400, "Missing required field \"op\"")
+ op = data.op
try:
if op == 'save':
- try:
- path = command['file']
- except KeyError:
+ if data.file:
+ path = data.file
+ else:
path = '/config/config.boot'
res = session.save_config(path)
elif op == 'load':
- try:
- path = command['file']
- except KeyError:
+ if data.file:
+ path = data.file
+ else:
return error(400, "Missing required field \"file\"")
res = session.migrate_and_load_config(path)
res = session.commit()
@@ -268,33 +532,28 @@ def config_file_op(command):
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
- print(traceback.format_exc(), file=sys.stderr)
+ logger.critical(traceback.format_exc())
return error(500, "An internal error occured. Check the logs for details.")
return success(res)
-@app.route('/image', methods=['POST'])
-@get_command
-@auth_required
-def image_op(command):
- session = app.config['vyos_session']
+@app.post('/image')
+def image_op(data: ImageModel):
+ session = app.state.vyos_session
- try:
- op = command['op']
- except KeyError:
- return error(400, "Missing required field \"op\"")
+ op = data.op
try:
if op == 'add':
- try:
- url = command['url']
- except KeyError:
+ if data.url:
+ url = data.url
+ else:
return error(400, "Missing required field \"url\"")
res = session.install_image(url)
elif op == 'delete':
- try:
- name = command['name']
- except KeyError:
+ if data.name:
+ name = data.name
+ else:
return error(400, "Missing required field \"name\"")
res = session.remove_image(name)
else:
@@ -302,26 +561,17 @@ def image_op(command):
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
- print(traceback.format_exc(), file=sys.stderr)
+ logger.critical(traceback.format_exc())
return error(500, "An internal error occured. Check the logs for details.")
return success(res)
+@app.post('/generate')
+def generate_op(data: GenerateModel):
+ session = app.state.vyos_session
-@app.route('/generate', methods=['POST'])
-@get_command
-@auth_required
-def generate_op(command):
- session = app.config['vyos_session']
-
- try:
- op = command['op']
- path = command['path']
- except KeyError:
- return error(400, "Missing required field. \"op\" and \"path\" fields are required")
-
- if not isinstance(path, list):
- return error(400, "Malformed command: \"path\" field must be a list of strings")
+ op = data.op
+ path = data.path
try:
if op == 'generate':
@@ -331,25 +581,17 @@ def generate_op(command):
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
- print(traceback.format_exc(), file=sys.stderr)
+ logger.critical(traceback.format_exc())
return error(500, "An internal error occured. Check the logs for details.")
return success(res)
-@app.route('/show', methods=['POST'])
-@get_command
-@auth_required
-def show_op(command):
- session = app.config['vyos_session']
+@app.post('/show')
+def show_op(data: ShowModel):
+ session = app.state.vyos_session
- try:
- op = command['op']
- path = command['path']
- except KeyError:
- return error(400, "Missing required field. \"op\" and \"path\" fields are required")
-
- if not isinstance(path, list):
- return error(400, "Malformed command: \"path\" field must be a list of strings")
+ op = data.op
+ path = data.path
try:
if op == 'show':
@@ -359,13 +601,24 @@ def show_op(command):
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
- print(traceback.format_exc(), file=sys.stderr)
+ logger.critical(traceback.format_exc())
return error(500, "An internal error occured. Check the logs for details.")
return success(res)
-def shutdown():
- raise KeyboardInterrupt
+###
+# GraphQL integration
+###
+
+from api.graphql.bindings import generate_schema
+
+api.graphql.state.init()
+
+schema = generate_schema()
+
+app.add_route('/graphql', GraphQL(schema, debug=True))
+
+###
if __name__ == '__main__':
# systemd's user and group options don't work, do it by hand here,
@@ -379,22 +632,23 @@ if __name__ == '__main__':
try:
server_config = load_server_config()
- except Exception as e:
- print("Failed to load the HTTP API server config: {0}".format(e))
+ except Exception as err:
+ logger.critical(f"Failed to load the HTTP API server config: {err}")
- session = ConfigSession(os.getpid())
+ config_session = ConfigSession(os.getpid())
- app.config['vyos_session'] = session
- app.config['vyos_keys'] = server_config['api_keys']
- app.config['vyos_debug'] = server_config['debug']
+ app.state.vyos_session = config_session
+ app.state.vyos_keys = server_config['api_keys']
- def sig_handler(signum, frame):
- shutdown()
+ app.state.vyos_debug = bool(server_config['debug'] == 'true')
+ app.state.vyos_strict = bool(server_config['strict'] == 'true')
- signal.signal(signal.SIGTERM, sig_handler)
+ api.graphql.state.settings['app'] = app
try:
- serve(app, host=server_config["listen_address"],
- port=server_config["port"])
- except OSError as e:
- print(f"OSError {e}")
+ uvicorn.run(app, host=server_config["listen_address"],
+ port=int(server_config["port"]),
+ proxy_headers=True)
+ except OSError as err:
+ logger.critical(f"OSError {err}")
+ sys.exit(1)
diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py
index 7e2076820..1fba0d75b 100755
--- a/src/system/keepalived-fifo.py
+++ b/src/system/keepalived-fifo.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,7 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
import os
import time
@@ -22,11 +21,13 @@ import argparse
import threading
import re
import json
-from pathlib import Path
-from queue import Queue
import logging
+
+from queue import Queue
from logging.handlers import SysLogHandler
+from vyos.ifconfig.vrrp import VRRP
+from vyos.configquery import ConfigTreeQuery
from vyos.util import cmd
# configure logging
@@ -37,17 +38,20 @@ logs_handler_syslog.setFormatter(logs_format)
logger.addHandler(logs_handler_syslog)
logger.setLevel(logging.DEBUG)
+mdns_running_file = '/run/mdns_vrrp_active'
+mdns_update_command = 'sudo /usr/libexec/vyos/conf_mode/service_mdns-repeater.py'
# class for all operations
class KeepalivedFifo:
# init - read command arguments
def __init__(self):
- logger.info("Starting FIFO pipe for Keepalived")
+ logger.info('Starting FIFO pipe for Keepalived')
# define program arguments
cmd_args_parser = argparse.ArgumentParser(description='Create FIFO pipe for keepalived and process notify events', add_help=False)
cmd_args_parser.add_argument('PIPE', help='path to the FIFO pipe')
# parse arguments
cmd_args = cmd_args_parser.parse_args()
+
self._config_load()
self.pipe_path = cmd_args.PIPE
@@ -59,33 +63,34 @@ class KeepalivedFifo:
# load configuration
def _config_load(self):
try:
- # read the dictionary file with configuration
- with open('/run/keepalived_config.dict', 'r') as dict_file:
- vrrp_config_dict = json.load(dict_file)
+ base = ['high-availability', 'vrrp']
+ conf = ConfigTreeQuery()
+ if not conf.exists(base):
+ raise ValueError()
+
+ # Read VRRP configuration directly from CLI
+ vrrp_config_dict = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True)
self.vrrp_config = {'vrrp_groups': {}, 'sync_groups': {}}
- # save VRRP instances to the new dictionary
- for vrrp_group in vrrp_config_dict['vrrp_groups']:
- self.vrrp_config['vrrp_groups'][vrrp_group['name']] = {
- 'STOP': vrrp_group.get('stop_script'),
- 'FAULT': vrrp_group.get('fault_script'),
- 'BACKUP': vrrp_group.get('backup_script'),
- 'MASTER': vrrp_group.get('master_script')
- }
- # save VRRP sync groups to the new dictionary
- for sync_group in vrrp_config_dict['sync_groups']:
- self.vrrp_config['sync_groups'][sync_group['name']] = {
- 'STOP': sync_group.get('stop_script'),
- 'FAULT': sync_group.get('fault_script'),
- 'BACKUP': sync_group.get('backup_script'),
- 'MASTER': sync_group.get('master_script')
- }
- logger.debug("Loaded configuration: {}".format(self.vrrp_config))
+ for key in ['group', 'sync_group']:
+ if key not in vrrp_config_dict:
+ continue
+ for group, group_config in vrrp_config_dict[key].items():
+ if 'transition_script' not in group_config:
+ continue
+ self.vrrp_config['vrrp_groups'][group] = {
+ 'STOP': group_config['transition_script'].get('stop'),
+ 'FAULT': group_config['transition_script'].get('fault'),
+ 'BACKUP': group_config['transition_script'].get('backup'),
+ 'MASTER': group_config['transition_script'].get('master'),
+ }
+ logger.info(f'Loaded configuration: {self.vrrp_config}')
except Exception as err:
- logger.error("Unable to load configuration: {}".format(err))
+ logger.error(f'Unable to load configuration: {err}')
# run command
def _run_command(self, command):
- logger.debug("Running the command: {}".format(command))
+ logger.debug(f'Running the command: {command}')
try:
cmd(command)
except OSError as err:
@@ -93,14 +98,14 @@ class KeepalivedFifo:
# create FIFO pipe
def pipe_create(self):
- if Path(self.pipe_path).exists():
- logger.info("PIPE already exist: {}".format(self.pipe_path))
+ if os.path.exists(self.pipe_path):
+ logger.info(f'PIPE already exist: {self.pipe_path}')
else:
os.mkfifo(self.pipe_path)
# process message from pipe
def pipe_process(self):
- logger.debug("Message processing start")
+ logger.debug('Message processing start')
regex_notify = re.compile(r'^(?P<type>\w+) "(?P<name>[\w-]+)" (?P<state>\w+) (?P<priority>\d+)$', re.MULTILINE)
while self.stopme.is_set() is False:
# wait for a new message event from pipe_wait
@@ -111,16 +116,19 @@ class KeepalivedFifo:
# get all messages from queue and try to process them
while self.message_queue.empty() is not True:
message = self.message_queue.get()
- logger.debug("Received message: {}".format(message))
+ logger.debug(f'Received message: {message}')
notify_message = regex_notify.search(message)
# try to process a message if it looks valid
if notify_message:
n_type = notify_message.group('type')
n_name = notify_message.group('name')
n_state = notify_message.group('state')
- logger.info("{} {} changed state to {}".format(n_type, n_name, n_state))
+ logger.info(f'{n_type} {n_name} changed state to {n_state}')
# check and run commands for VRRP instances
if n_type == 'INSTANCE':
+ if os.path.exists(mdns_running_file):
+ cmd(mdns_update_command)
+
if n_name in self.vrrp_config['vrrp_groups'] and n_state in self.vrrp_config['vrrp_groups'][n_name]:
n_script = self.vrrp_config['vrrp_groups'][n_name].get(n_state)
if n_script:
@@ -128,6 +136,9 @@ class KeepalivedFifo:
# check and run commands for VRRP sync groups
# currently, this is not available in VyOS CLI
if n_type == 'GROUP':
+ if os.path.exists(mdns_running_file):
+ cmd(mdns_update_command)
+
if n_name in self.vrrp_config['sync_groups'] and n_state in self.vrrp_config['sync_groups'][n_name]:
n_script = self.vrrp_config['sync_groups'][n_name].get(n_state)
if n_script:
@@ -135,16 +146,16 @@ class KeepalivedFifo:
# mark task in queue as done
self.message_queue.task_done()
except Exception as err:
- logger.error("Error processing message: {}".format(err))
- logger.debug("Terminating messages processing thread")
+ logger.error(f'Error processing message: {err}')
+ logger.debug('Terminating messages processing thread')
# wait for messages
def pipe_wait(self):
- logger.debug("Message reading start")
+ logger.debug('Message reading start')
self.pipe_read = os.open(self.pipe_path, os.O_RDONLY | os.O_NONBLOCK)
while self.stopme.is_set() is False:
# sleep a bit to not produce 100% CPU load
- time.sleep(0.1)
+ time.sleep(0.250)
try:
# try to read a message from PIPE
message = os.read(self.pipe_read, 500)
@@ -157,21 +168,19 @@ class KeepalivedFifo:
except Exception as err:
# ignore the "Resource temporarily unavailable" error
if err.errno != 11:
- logger.error("Error receiving message: {}".format(err))
+ logger.error(f'Error receiving message: {err}')
- logger.debug("Closing FIFO pipe")
+ logger.debug('Closing FIFO pipe')
os.close(self.pipe_read)
-
# handle SIGTERM signal to allow finish all messages processing
def sigterm_handle(signum, frame):
- logger.info("Ending processing: Received SIGTERM signal")
+ logger.info('Ending processing: Received SIGTERM signal')
fifo.stopme.set()
thread_wait_message.join()
fifo.message_event.set()
thread_process_message.join()
-
signal.signal(signal.SIGTERM, sigterm_handle)
# init our class
diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh
index a062dc810..49e53d7e1 100755
--- a/src/system/on-dhcp-event.sh
+++ b/src/system/on-dhcp-event.sh
@@ -21,21 +21,20 @@ client_mac=$4
domain=$5
hostsd_client="/usr/bin/vyos-hostsd-client"
-if [ -z "$client_name" ]; then
- logger -s -t on-dhcp-event "Client name was empty, using MAC \"$client_mac\" instead"
- client_name=$(echo "client-"$client_mac | tr : -)
-fi
-
-if [ "$domain" == "..YYZ!" ]; then
- client_fqdn_name=$client_name
- client_search_expr=$client_name
-else
- client_fqdn_name=$client_name.$domain
- client_search_expr="$client_name\\.$domain"
-fi
-
case "$action" in
commit) # add mapping for new lease
+ if [ -z "$client_name" ]; then
+ logger -s -t on-dhcp-event "Client name was empty, using MAC \"$client_mac\" instead"
+ client_name=$(echo "client-"$client_mac | tr : -)
+ fi
+
+ if [ "$domain" == "..YYZ!" ]; then
+ client_fqdn_name=$client_name
+ client_search_expr=$client_name
+ else
+ client_fqdn_name=$client_name.$domain
+ client_search_expr="$client_name\\.$domain"
+ fi
$hostsd_client --add-hosts "$client_fqdn_name,$client_ip" --tag "dhcp-server-$client_ip" --apply
exit 0
;;
diff --git a/src/systemd/vyos-http-api.service b/src/systemd/vyos-http-api.service
index 4fa68b4ff..ba5df5984 100644
--- a/src/systemd/vyos-http-api.service
+++ b/src/systemd/vyos-http-api.service
@@ -5,9 +5,8 @@ Requires=vyos-router.service
[Service]
ExecStartPre=/usr/libexec/vyos/init/vyos-config
-ExecStart=/usr/bin/python3 -u /usr/libexec/vyos/services/vyos-http-api-server
+ExecStart=/usr/libexec/vyos/services/vyos-http-api-server
Type=idle
-KillMode=process
SyslogIdentifier=vyos-http-api
SyslogFacility=daemon
diff --git a/src/validators/allowed-vlan b/src/validators/allowed-vlan
new file mode 100755
index 000000000..11389390b
--- /dev/null
+++ b/src/validators/allowed-vlan
@@ -0,0 +1,19 @@
+#! /usr/bin/python3
+
+import sys
+import re
+
+if __name__ == '__main__':
+ if len(sys.argv)>1:
+ allowed_vlan = sys.argv[1]
+ if re.search('[0-9]{1,4}-[0-9]{1,4}', allowed_vlan):
+ for tmp in allowed_vlan.split('-'):
+ if int(tmp) not in range(1, 4095):
+ sys.exit(1)
+ else:
+ if int(allowed_vlan) not in range(1, 4095):
+ sys.exit(1)
+ else:
+ sys.exit(2)
+
+ sys.exit(0)
diff --git a/src/validators/base64 b/src/validators/base64
new file mode 100755
index 000000000..e2b1e730d
--- /dev/null
+++ b/src/validators/base64
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import base64
+from sys import argv
+
+if __name__ == '__main__':
+ if len(argv) != 2:
+ exit(1)
+ try:
+ base64.b64decode(argv[1])
+ except:
+ exit(1)
+ exit(0)
diff --git a/src/validators/script b/src/validators/script
index 2665ec1f6..1d8a27e5c 100755
--- a/src/validators/script
+++ b/src/validators/script
@@ -1,8 +1,6 @@
#!/usr/bin/env python3
#
-# numeric value validator
-#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -23,7 +21,6 @@ import shlex
import vyos.util
-
if __name__ == '__main__':
if len(sys.argv) < 2:
sys.exit('Please specify script file to check')
@@ -35,11 +32,11 @@ if __name__ == '__main__':
sys.exit(f'File {script} does not exist')
if not (os.path.isfile(script) and os.access(script, os.X_OK)):
- sys.exit('File {script} is not an executable file')
+ sys.exit(f'File {script} is not an executable file')
# File outside the config dir is just a warning
if not vyos.util.file_is_persistent(script):
sys.exit(
- 'Warning: file {path} is outside the / config directory\n'
+ f'Warning: file {path} is outside the / config directory\n'
'It will not be automatically migrated to a new image on system update'
)