summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg <georg@lysergic.dev>2022-04-08 14:52:37 +0000
committerGitHub <noreply@github.com>2022-04-08 14:52:37 +0000
commit630945291c9a389ad62fd32caea3749f4c5e9d72 (patch)
treea85f72880269bfb43740b7a0bc790dcaca6de1e7
parent15461be0cd7b51e0e290d66bae0bb112f6b2c3ea (diff)
parent654dbc9aa3b0d27ec4f3faefff6cbd85fc3e1d1a (diff)
downloadvyos-1x-630945291c9a389ad62fd32caea3749f4c5e9d72.tar.gz
vyos-1x-630945291c9a389ad62fd32caea3749f4c5e9d72.zip
Merge branch 'current' into dhcpd
-rw-r--r--Makefile6
-rw-r--r--data/configd-include.json2
-rw-r--r--data/templates/accel-ppp/ipoe.config.tmpl28
-rw-r--r--data/templates/bcast-relay/udp-broadcast-relay.tmpl6
-rw-r--r--data/templates/conntrack/nftables-ct.tmpl48
-rw-r--r--data/templates/conntrack/sysctl.conf.tmpl2
-rw-r--r--data/templates/containers/registry.tmpl2
-rw-r--r--data/templates/dhcp-client/daemon-options.tmpl2
-rw-r--r--data/templates/dhcp-client/ipv4.tmpl8
-rw-r--r--data/templates/dhcp-client/ipv6.tmpl34
-rw-r--r--data/templates/dhcp-relay/dhcrelay.conf.tmpl2
-rw-r--r--data/templates/dhcp-relay/dhcrelay6.conf.tmpl10
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl108
-rw-r--r--data/templates/dhcp-server/dhcpdv6.conf.tmpl66
-rw-r--r--data/templates/dns-forwarding/recursor.conf.tmpl3
-rw-r--r--data/templates/firewall/nftables-defines.tmpl32
-rw-r--r--data/templates/firewall/nftables-policy.tmpl24
-rw-r--r--data/templates/firewall/nftables.tmpl90
-rw-r--r--data/templates/firewall/upnpd.conf.tmpl172
-rw-r--r--data/templates/frr/bfdd.frr.tmpl26
-rw-r--r--data/templates/frr/bgpd.frr.tmpl439
-rw-r--r--data/templates/frr/isisd.frr.tmpl177
-rw-r--r--data/templates/frr/ldpd.frr.tmpl253
-rw-r--r--data/templates/frr/ospf6d.frr.tmpl76
-rw-r--r--data/templates/frr/ospfd.frr.tmpl159
-rw-r--r--data/templates/frr/policy.frr.tmpl199
-rw-r--r--data/templates/frr/rip_ripng.frr.j218
-rw-r--r--data/templates/frr/ripd.frr.tmpl62
-rw-r--r--data/templates/frr/ripngd.frr.tmpl46
-rw-r--r--data/templates/frr/rpki.frr.tmpl6
-rw-r--r--data/templates/frr/static_routes_macro.j221
-rw-r--r--data/templates/frr/staticd.frr.tmpl24
-rw-r--r--data/templates/frr/vrf-vni.frr.tmpl4
-rw-r--r--data/templates/frr/vrf.route-map.frr.tmpl4
-rw-r--r--data/templates/high-availability/keepalived.conf.tmpl18
-rw-r--r--data/templates/https/nginx.default.tmpl13
-rw-r--r--data/templates/ipsec/charon.tmpl11
-rw-r--r--data/templates/ipsec/swanctl/peer.tmpl25
-rw-r--r--data/templates/lldp/lldpd.tmpl3
-rw-r--r--data/templates/lldp/vyos.conf.tmpl35
-rw-r--r--data/templates/monitoring/override.conf.tmpl2
-rw-r--r--data/templates/monitoring/telegraf.tmpl11
-rw-r--r--data/templates/nhrp/opennhrp.conf.tmpl2
-rw-r--r--data/templates/ntp/ntpd.conf.tmpl1
-rw-r--r--data/templates/openvpn/server.conf.tmpl10
-rw-r--r--data/templates/pmacct/override.conf.tmpl (renamed from src/etc/systemd/system/uacctd.service.d/override.conf)5
-rw-r--r--data/templates/pmacct/uacctd.conf.tmpl (renamed from data/templates/netflow/uacctd.conf.tmpl)10
-rw-r--r--data/templates/ssh/sshd_config.tmpl1
-rw-r--r--data/templates/syslog/logrotate.tmpl9
-rw-r--r--data/templates/zone_policy/nftables.tmpl30
-rw-r--r--debian/control4
-rw-r--r--debian/vyos-1x.install1
-rw-r--r--debian/vyos-1x.postinst14
-rw-r--r--debian/vyos-1x.preinst2
-rw-r--r--interface-definitions/containers.xml.in31
-rw-r--r--interface-definitions/dhcp-relay.xml.in7
-rw-r--r--interface-definitions/dhcp-server.xml.in2
-rw-r--r--interface-definitions/dhcpv6-relay.xml.in2
-rw-r--r--interface-definitions/dns-domain-name.xml.in1
-rw-r--r--interface-definitions/dns-forwarding.xml.in19
-rw-r--r--interface-definitions/firewall.xml.in236
-rw-r--r--interface-definitions/flow-accounting-conf.xml.in27
-rw-r--r--interface-definitions/high-availability.xml.in53
-rw-r--r--interface-definitions/igmp-proxy.xml.in8
-rw-r--r--interface-definitions/include/accel-ppp/client-ip-pool-subnet-single.xml.i15
-rw-r--r--interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i2
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions.xml.i6
-rw-r--r--interface-definitions/include/arp-ndp-table-size.xml.i14
-rw-r--r--interface-definitions/include/bfd/common.xml.i6
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i8
-rw-r--r--interface-definitions/include/bgp/timers-keepalive.xml.i2
-rw-r--r--interface-definitions/include/conntrack/log-common.xml.i20
-rw-r--r--interface-definitions/include/conntrack/timeout-common-protocols.xml.i172
-rw-r--r--interface-definitions/include/firewall/common-rule.xml.i49
-rw-r--r--interface-definitions/include/firewall/icmp-type-name.xml.i142
-rw-r--r--interface-definitions/include/firewall/icmpv6-type-name.xml.i73
-rw-r--r--interface-definitions/include/firewall/mac-group.xml.i10
-rw-r--r--interface-definitions/include/firewall/name-default-action.xml.i2
-rw-r--r--interface-definitions/include/firewall/port.xml.i5
-rw-r--r--interface-definitions/include/firewall/source-destination-group-ipv6.xml.i1
-rw-r--r--interface-definitions/include/firewall/source-destination-group.xml.i1
-rw-r--r--interface-definitions/include/firewall/tcp-flags.xml.i119
-rw-r--r--interface-definitions/include/interface/arp-cache-timeout.xml.i2
-rw-r--r--interface-definitions/include/interface/dhcp-options.xml.i3
-rw-r--r--interface-definitions/include/interface/dhcpv6-options.xml.i4
-rw-r--r--interface-definitions/include/interface/inbound-interface.xml.i10
-rw-r--r--interface-definitions/include/interface/interface-policy-vif-c.xml.i4
-rw-r--r--interface-definitions/include/interface/interface-policy-vif.xml.i4
-rw-r--r--interface-definitions/include/interface/interface-policy.xml.i4
-rw-r--r--interface-definitions/include/interface/redirect.xml.i17
-rw-r--r--interface-definitions/include/interface/tunnel-remote-multi.xml.i19
-rw-r--r--interface-definitions/include/interface/tunnel-remote.xml.i2
-rw-r--r--interface-definitions/include/interface/vif-s.xml.i4
-rw-r--r--interface-definitions/include/interface/vif.xml.i4
-rw-r--r--interface-definitions/include/ipsec/local-traffic-selector.xml.i4
-rw-r--r--interface-definitions/include/isis/high-low-label-value.xml.i26
-rw-r--r--interface-definitions/include/isis/password.xml.i20
-rw-r--r--interface-definitions/include/isis/protocol-common-config.xml.i110
-rw-r--r--interface-definitions/include/nat-port.xml.i7
-rw-r--r--interface-definitions/include/nat-translation-options.xml.i10
-rw-r--r--interface-definitions/include/nat-translation-port.xml.i3
-rw-r--r--interface-definitions/include/ospf/auto-cost.xml.i2
-rw-r--r--interface-definitions/include/ospf/interface-common.xml.i2
-rw-r--r--interface-definitions/include/ospf/intervals.xml.i8
-rw-r--r--interface-definitions/include/ospf/metric-type.xml.i2
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i60
-rw-r--r--interface-definitions/include/ospfv3/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/policy/route-common-rule-ipv6.xml.i24
-rw-r--r--interface-definitions/include/policy/route-common-rule.xml.i24
-rw-r--r--interface-definitions/include/qos/bandwidth.xml.i15
-rw-r--r--interface-definitions/include/qos/burst.xml.i16
-rw-r--r--interface-definitions/include/qos/codel-quantum.xml.i16
-rw-r--r--interface-definitions/include/qos/dscp.xml.i143
-rw-r--r--interface-definitions/include/qos/flows.xml.i16
-rw-r--r--interface-definitions/include/qos/hfsc-d.xml.i15
-rw-r--r--interface-definitions/include/qos/hfsc-m1.xml.i32
-rw-r--r--interface-definitions/include/qos/hfsc-m2.xml.i32
-rw-r--r--interface-definitions/include/qos/interval.xml.i16
-rw-r--r--interface-definitions/include/qos/match.xml.i221
-rw-r--r--interface-definitions/include/qos/max-length.xml.i15
-rw-r--r--interface-definitions/include/qos/queue-limit-1-4294967295.xml.i15
-rw-r--r--interface-definitions/include/qos/queue-limit-2-10999.xml.i16
-rw-r--r--interface-definitions/include/qos/queue-type.xml.i30
-rw-r--r--interface-definitions/include/qos/set-dscp.xml.i63
-rw-r--r--interface-definitions/include/qos/target.xml.i16
-rw-r--r--interface-definitions/include/qos/tcp-flags.xml.i21
-rw-r--r--interface-definitions/include/radius-server-port.xml.i2
-rw-r--r--interface-definitions/include/rip/rip-timers.xml.i6
-rw-r--r--interface-definitions/include/snmp/access-mode.xml.i2
-rw-r--r--interface-definitions/include/snmp/authentication-type.xml.i2
-rw-r--r--interface-definitions/include/snmp/privacy-type.xml.i2
-rw-r--r--interface-definitions/include/snmp/protocol.xml.i2
-rw-r--r--interface-definitions/include/ssh-user.xml.i4
-rw-r--r--interface-definitions/include/static/static-route-blackhole.xml.i3
-rw-r--r--interface-definitions/include/static/static-route-reject.xml.i12
-rw-r--r--interface-definitions/include/static/static-route-tag.xml.i14
-rw-r--r--interface-definitions/include/static/static-route.xml.i24
-rw-r--r--interface-definitions/include/static/static-route6.xml.i24
-rw-r--r--interface-definitions/include/version/bgp-version.xml.i3
-rw-r--r--interface-definitions/include/version/broadcast-relay-version.xml.i3
-rw-r--r--interface-definitions/include/version/cluster-version.xml.i3
-rw-r--r--interface-definitions/include/version/config-management-version.xml.i3
-rw-r--r--interface-definitions/include/version/conntrack-sync-version.xml.i3
-rw-r--r--interface-definitions/include/version/conntrack-version.xml.i3
-rw-r--r--interface-definitions/include/version/dhcp-relay-version.xml.i3
-rw-r--r--interface-definitions/include/version/dhcp-server-version.xml.i3
-rw-r--r--interface-definitions/include/version/dhcpv6-server-version.xml.i3
-rw-r--r--interface-definitions/include/version/dns-forwarding-version.xml.i3
-rw-r--r--interface-definitions/include/version/firewall-version.xml.i3
-rw-r--r--interface-definitions/include/version/flow-accounting-version.xml.i3
-rw-r--r--interface-definitions/include/version/https-version.xml.i3
-rw-r--r--interface-definitions/include/version/interfaces-version.xml.i3
-rw-r--r--interface-definitions/include/version/ipoe-server-version.xml.i3
-rw-r--r--interface-definitions/include/version/ipsec-version.xml.i3
-rw-r--r--interface-definitions/include/version/isis-version.xml.i3
-rw-r--r--interface-definitions/include/version/l2tp-version.xml.i3
-rw-r--r--interface-definitions/include/version/lldp-version.xml.i3
-rw-r--r--interface-definitions/include/version/mdns-version.xml.i3
-rw-r--r--interface-definitions/include/version/nat-version.xml.i3
-rw-r--r--interface-definitions/include/version/nat66-version.xml.i3
-rw-r--r--interface-definitions/include/version/ntp-version.xml.i3
-rw-r--r--interface-definitions/include/version/openconnect-version.xml.i3
-rw-r--r--interface-definitions/include/version/ospf-version.xml.i3
-rw-r--r--interface-definitions/include/version/policy-version.xml.i3
-rw-r--r--interface-definitions/include/version/pppoe-server-version.xml.i3
-rw-r--r--interface-definitions/include/version/pptp-version.xml.i3
-rw-r--r--interface-definitions/include/version/qos-version.xml.i3
-rw-r--r--interface-definitions/include/version/quagga-version.xml.i3
-rw-r--r--interface-definitions/include/version/rpki-version.xml.i3
-rw-r--r--interface-definitions/include/version/salt-version.xml.i3
-rw-r--r--interface-definitions/include/version/snmp-version.xml.i3
-rw-r--r--interface-definitions/include/version/ssh-version.xml.i3
-rw-r--r--interface-definitions/include/version/sstp-version.xml.i3
-rw-r--r--interface-definitions/include/version/system-version.xml.i3
-rw-r--r--interface-definitions/include/version/vrf-version.xml.i3
-rw-r--r--interface-definitions/include/version/vrrp-version.xml.i3
-rw-r--r--interface-definitions/include/version/vyos-accel-ppp-version.xml.i3
-rw-r--r--interface-definitions/include/version/wanloadbalance-version.xml.i3
-rw-r--r--interface-definitions/include/version/webproxy-version.xml.i3
-rw-r--r--interface-definitions/include/vpn-ipsec-encryption.xml.i3
-rw-r--r--interface-definitions/include/vpn-ipsec-hash.xml.i3
-rw-r--r--interface-definitions/interfaces-bonding.xml.in7
-rw-r--r--interface-definitions/interfaces-bridge.xml.in13
-rw-r--r--interface-definitions/interfaces-dummy.xml.in2
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in5
-rw-r--r--interface-definitions/interfaces-geneve.xml.in2
-rw-r--r--interface-definitions/interfaces-input.xml.in29
-rw-r--r--interface-definitions/interfaces-l2tpv3.xml.in8
-rw-r--r--interface-definitions/interfaces-loopback.xml.in2
-rw-r--r--interface-definitions/interfaces-macsec.xml.in10
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in24
-rw-r--r--interface-definitions/interfaces-pppoe.xml.in6
-rw-r--r--interface-definitions/interfaces-pseudo-ethernet.xml.in2
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in8
-rw-r--r--interface-definitions/interfaces-vti.xml.in2
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in4
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in5
-rw-r--r--interface-definitions/interfaces-wireless.xml.in16
-rw-r--r--interface-definitions/interfaces-wwan.xml.in6
-rw-r--r--interface-definitions/lldp.xml.in22
-rw-r--r--interface-definitions/policy-local-route.xml.in111
-rw-r--r--interface-definitions/policy-route.xml.in32
-rw-r--r--interface-definitions/policy.xml.in32
-rw-r--r--interface-definitions/protocols-rpki.xml.in2
-rw-r--r--interface-definitions/qos.xml.in789
-rw-r--r--interface-definitions/service_console-server.xml.in6
-rw-r--r--interface-definitions/service_ipoe-server.xml.in16
-rw-r--r--interface-definitions/service_monitoring_telegraf.xml.in10
-rw-r--r--interface-definitions/service_router-advert.xml.in14
-rw-r--r--interface-definitions/service_upnp.xml.in224
-rw-r--r--interface-definitions/service_webproxy.xml.in27
-rw-r--r--interface-definitions/snmp.xml.in6
-rw-r--r--interface-definitions/ssh.xml.in10
-rw-r--r--interface-definitions/system-conntrack.xml.in330
-rw-r--r--interface-definitions/system-ip.xml.in16
-rw-r--r--interface-definitions/system-ipv6.xml.in21
-rw-r--r--interface-definitions/system-login.xml.in4
-rw-r--r--interface-definitions/system-logs.xml.in8
-rw-r--r--interface-definitions/vpn_ipsec.xml.in117
-rw-r--r--interface-definitions/vpn_l2tp.xml.in10
-rw-r--r--interface-definitions/vpn_openconnect.xml.in16
-rw-r--r--interface-definitions/xml-component-version.xml.in44
-rw-r--r--interface-definitions/zone-policy.xml.in10
-rw-r--r--op-mode-definitions/generate-openvpn-config-client.xml.in58
-rw-r--r--op-mode-definitions/include/bgp/afi-ipv4-ipv6-flowspec.xml.i25
-rw-r--r--op-mode-definitions/include/bgp/show-bgp-common.xml.i1
-rw-r--r--op-mode-definitions/include/ospf-common.xml.i9
-rw-r--r--op-mode-definitions/monitor-log.xml.in73
-rw-r--r--op-mode-definitions/policy-route.xml.in8
-rw-r--r--op-mode-definitions/reboot.xml.in4
-rw-r--r--op-mode-definitions/show-log.xml.in73
-rw-r--r--op-mode-definitions/show-virtual-server.xml.in13
-rw-r--r--python/vyos/base.py6
-rw-r--r--python/vyos/configdict.py87
-rw-r--r--python/vyos/configdiff.py84
-rw-r--r--python/vyos/configtree.py96
-rw-r--r--python/vyos/configverify.py56
-rw-r--r--python/vyos/defaults.py1
-rw-r--r--python/vyos/ethtool.py9
-rw-r--r--python/vyos/firewall.py85
-rw-r--r--python/vyos/frr.py22
-rw-r--r--python/vyos/ifconfig/bridge.py12
-rwxr-xr-xpython/vyos/ifconfig/interface.py148
-rw-r--r--python/vyos/ifconfig/loopback.py12
-rw-r--r--python/vyos/ifconfig/vxlan.py19
-rw-r--r--python/vyos/ifconfig/wireless.py6
-rw-r--r--python/vyos/migrator.py2
-rw-r--r--python/vyos/pki.py26
-rw-r--r--python/vyos/remote.py10
-rw-r--r--python/vyos/systemversions.py7
-rw-r--r--python/vyos/template.py101
-rw-r--r--python/vyos/util.py40
-rw-r--r--python/vyos/validate.py32
-rw-r--r--python/vyos/xml/__init__.py4
-rw-r--r--python/vyos/xml/definition.py9
-rwxr-xr-xscripts/build-command-templates17
-rw-r--r--smoketest/configs/basic-vyos88
-rw-r--r--smoketest/configs/dialup-router-complex19
-rw-r--r--smoketest/configs/dialup-router-medium-vpn33
-rw-r--r--smoketest/configs/ipv6-disable83
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py32
-rwxr-xr-xsmoketest/scripts/cli/test_component_version.py36
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py31
-rwxr-xr-xsmoketest/scripts/cli/test_ha_vrrp.py30
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py19
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py129
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pseudo_ethernet.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py106
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py36
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py2
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py365
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py34
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py199
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py35
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py30
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py36
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static.py57
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py5
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py78
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py127
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_telegraf.py2
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py72
-rwxr-xr-xsmoketest/scripts/cli/test_service_upnp.py105
-rwxr-xr-xsmoketest/scripts/cli/test_service_webproxy.py16
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py5
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py17
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py10
-rwxr-xr-xsmoketest/scripts/cli/test_system_logs.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py60
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py80
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py67
-rwxr-xr-xsmoketest/scripts/cli/test_zone_policy.py18
-rwxr-xr-xsrc/conf_mode/conntrack.py23
-rwxr-xr-xsrc/conf_mode/conntrack_sync.py6
-rwxr-xr-xsrc/conf_mode/containers.py50
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py25
-rwxr-xr-xsrc/conf_mode/firewall-interface.py13
-rwxr-xr-xsrc/conf_mode/firewall.py130
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py14
-rwxr-xr-xsrc/conf_mode/http-api.py9
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py8
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py3
-rwxr-xr-xsrc/conf_mode/interfaces-dummy.py2
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py24
-rwxr-xr-xsrc/conf_mode/interfaces-geneve.py2
-rwxr-xr-xsrc/conf_mode/interfaces-l2tpv3.py2
-rwxr-xr-xsrc/conf_mode/interfaces-loopback.py2
-rwxr-xr-xsrc/conf_mode/interfaces-macsec.py2
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py36
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py2
-rwxr-xr-xsrc/conf_mode/interfaces-pseudo-ethernet.py2
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py91
-rwxr-xr-xsrc/conf_mode/interfaces-vti.py2
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py56
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py2
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py2
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py97
-rwxr-xr-xsrc/conf_mode/lldp.py235
-rwxr-xr-xsrc/conf_mode/nat.py16
-rwxr-xr-xsrc/conf_mode/policy-local-route.py205
-rwxr-xr-xsrc/conf_mode/policy-route-interface.py8
-rwxr-xr-xsrc/conf_mode/policy-route.py227
-rwxr-xr-xsrc/conf_mode/policy.py7
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py19
-rwxr-xr-xsrc/conf_mode/protocols_isis.py44
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py21
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py11
-rwxr-xr-xsrc/conf_mode/protocols_static.py4
-rwxr-xr-xsrc/conf_mode/qos.py87
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py23
-rwxr-xr-xsrc/conf_mode/service_monitoring_telegraf.py25
-rwxr-xr-xsrc/conf_mode/service_upnp.py157
-rwxr-xr-xsrc/conf_mode/system-ip.py43
-rwxr-xr-xsrc/conf_mode/system-ipv6.py99
-rwxr-xr-xsrc/conf_mode/system-login.py21
-rwxr-xr-xsrc/conf_mode/system-syslog.py14
-rwxr-xr-xsrc/conf_mode/vrf.py108
-rwxr-xr-xsrc/conf_mode/zone_policy.py24
-rw-r--r--src/etc/cron.d/check-wwan1
-rw-r--r--src/etc/logrotate.d/conntrackd9
-rw-r--r--src/etc/logrotate.d/vyos-rsyslog12
-rwxr-xr-xsrc/etc/telegraf/custom_scripts/show_firewall_input_filter.py73
-rwxr-xr-xsrc/etc/telegraf/custom_scripts/show_interfaces_input_filter.py123
-rwxr-xr-xsrc/helpers/strip-private.py10
-rwxr-xr-xsrc/helpers/system-versions-foot.py2
-rwxr-xr-xsrc/helpers/vyos-vrrp-conntracksync.sh8
-rwxr-xr-xsrc/helpers/vyos_net_name21
-rwxr-xr-xsrc/migration-scripts/bgp/0-to-12
-rwxr-xr-xsrc/migration-scripts/bgp/1-to-233
-rwxr-xr-xsrc/migration-scripts/dns-forwarding/1-to-283
-rwxr-xr-xsrc/migration-scripts/firewall/6-to-7182
-rwxr-xr-xsrc/migration-scripts/ipsec/8-to-948
-rwxr-xr-xsrc/migration-scripts/policy/1-to-286
-rwxr-xr-xsrc/migration-scripts/ssh/1-to-250
-rwxr-xr-xsrc/migration-scripts/system/22-to-2350
-rwxr-xr-xsrc/op_mode/cpu_summary.py36
-rwxr-xr-xsrc/op_mode/firewall.py18
-rwxr-xr-xsrc/op_mode/generate_ovpn_client_file.py145
-rwxr-xr-xsrc/op_mode/generate_public_key_command.py11
-rwxr-xr-xsrc/op_mode/lldp_op.py2
-rwxr-xr-xsrc/op_mode/monitor_bandwidth_test.sh3
-rwxr-xr-xsrc/op_mode/policy_route.py12
-rwxr-xr-xsrc/op_mode/powerctrl.py25
-rwxr-xr-xsrc/op_mode/show_cpu.py63
-rwxr-xr-xsrc/op_mode/show_ipsec_sa.py186
-rwxr-xr-xsrc/op_mode/show_ram.py19
-rwxr-xr-xsrc/op_mode/show_uptime.py27
-rwxr-xr-xsrc/op_mode/show_version.py22
-rwxr-xr-xsrc/op_mode/show_virtual_server.py33
-rwxr-xr-xsrc/op_mode/vrrp.py13
-rwxr-xr-xsrc/services/vyos-http-api-server6
-rwxr-xr-xsrc/system/keepalived-fifo.py3
-rw-r--r--src/systemd/miniupnpd.service13
-rw-r--r--src/tests/test_util.py5
-rwxr-xr-xsrc/validators/ip-address7
-rwxr-xr-xsrc/validators/ip-cidr7
-rwxr-xr-xsrc/validators/ip-host7
-rwxr-xr-xsrc/validators/ip-prefix7
-rwxr-xr-xsrc/validators/ip-protocol1
-rwxr-xr-xsrc/validators/ipv47
-rwxr-xr-xsrc/validators/ipv4-address7
-rwxr-xr-xsrc/validators/ipv4-host7
-rwxr-xr-xsrc/validators/ipv4-multicast7
-rwxr-xr-xsrc/validators/ipv4-prefix7
-rwxr-xr-xsrc/validators/ipv4-range13
-rwxr-xr-xsrc/validators/ipv67
-rwxr-xr-xsrc/validators/ipv6-address7
-rwxr-xr-xsrc/validators/ipv6-host7
-rwxr-xr-xsrc/validators/ipv6-multicast7
-rwxr-xr-xsrc/validators/ipv6-prefix7
-rwxr-xr-xsrc/validators/ipv6-range30
-rwxr-xr-xsrc/validators/mac-address-firewall27
-rwxr-xr-xsrc/validators/port-multi45
-rwxr-xr-xsrc/validators/port-range35
-rwxr-xr-xsrc/validators/tcp-flag17
-rw-r--r--test-requirements.txt1
399 files changed, 10160 insertions, 3508 deletions
diff --git a/Makefile b/Makefile
index 29744b323..dc1301100 100644
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,12 @@ interface_definitions: $(config_xml_obj)
# XXX: delete top level node.def's that now live in other packages
# IPSec VPN EAP-RADIUS does not support source-address
rm -rf $(TMPL_DIR)/vpn/ipsec/remote-access/radius/source-address
+
+ # T4284 neq QoS implementation is not yet live
+ find $(TMPL_DIR)/interfaces -name redirect -type d -exec rm -rf {} \;
+ rm -rf $(TMPL_DIR)/qos
+ rm -rf $(TMPL_DIR)/interfaces/input
+
# XXX: test if there are empty node.def files - this is not allowed as these
# could mask help strings or mandatory priority statements
find $(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'
diff --git a/data/configd-include.json b/data/configd-include.json
index 739f2f6c8..b77d48001 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -48,6 +48,7 @@
"protocols_ripng.py",
"protocols_static.py",
"protocols_static_multicast.py",
+"qos.py",
"salt-minion.py",
"service_console-server.py",
"service_ids_fastnetmon.py",
@@ -55,6 +56,7 @@
"service_mdns-repeater.py",
"service_pppoe-server.py",
"service_router-advert.py",
+"service_upnp.py",
"ssh.py",
"system-ip.py",
"system-ipv6.py",
diff --git a/data/templates/accel-ppp/ipoe.config.tmpl b/data/templates/accel-ppp/ipoe.config.tmpl
index 1cf2ab0be..92c2d5715 100644
--- a/data/templates/accel-ppp/ipoe.config.tmpl
+++ b/data/templates/accel-ppp/ipoe.config.tmpl
@@ -25,11 +25,21 @@ level=5
verbose=1
{% for interface in interfaces %}
{% if interface.vlan_mon %}
-interface=re:{{ interface.name }}\.\d+,{% else %}interface={{ interface.name }},{% endif %}shared={{ interface.shared }},mode={{ interface.mode }},ifcfg={{ interface.ifcfg }},range={{ interface.range }},start={{ interface.sess_start }},ipv6=1
+interface=re:{{ interface.name }}\.\d+,{% else %}interface={{ interface.name }},{% endif %}shared={{ interface.shared }},mode={{ interface.mode }},ifcfg={{ interface.ifcfg }}{{ ',range=' + interface.range if interface.range is defined and interface.range is not none }},start={{ interface.sess_start }},ipv6=1
{% endfor %}
-{% if auth_mode == 'noauth' %}
+{% if auth_mode == 'noauth' %}
noauth=1
-{% elif auth_mode == 'local' %}
+{% if client_named_ip_pool %}
+{% for pool in client_named_ip_pool %}
+{% if pool.subnet is defined %}
+ip-pool={{ pool.name }}
+{% endif %}
+{% if pool.gateway_address is defined %}
+gw-ip-address={{ pool.gateway_address }}/{{ pool.subnet.split('/')[1] }}
+{% endif %}
+{% endfor%}
+{% endif %}
+{% elif auth_mode == 'local' %}
username=ifname
password=csid
{% endif %}
@@ -61,6 +71,18 @@ verbose=1
[ipv6-dhcp]
verbose=1
+{% if client_named_ip_pool %}
+[ip-pool]
+{% for pool in client_named_ip_pool %}
+{% if pool.subnet is defined %}
+{{ pool.subnet }},name={{ pool.name }}
+{% endif %}
+{% if pool.gateway_address is defined %}
+gw-ip-address={{ pool.gateway_address }}/{{ pool.subnet.split('/')[1] }}
+{% endif %}
+{% endfor%}
+{% endif %}
+
{% if client_ipv6_pool %}
[ipv6-pool]
{% for p in client_ipv6_pool %}
diff --git a/data/templates/bcast-relay/udp-broadcast-relay.tmpl b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
index 73e9acad4..7b2b9b1a2 100644
--- a/data/templates/bcast-relay/udp-broadcast-relay.tmpl
+++ b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
@@ -1,7 +1,5 @@
### Autogenerated by bcast_relay.py ###
# UDP broadcast relay configuration for instance {{ id }}
-{% if description %}
-# Comment: {{ description }}
-{% endif %}
-DAEMON_ARGS="{{ '-s ' + address if address is defined }} {{ instance }} {{ port }} {{ interface | join(' ') }}"
+{{ '# ' ~ description if description is vyos_defined }}
+DAEMON_ARGS="{{ '-s ' ~ address if address is defined }} {{ instance }} {{ port }} {{ interface | join(' ') }}"
diff --git a/data/templates/conntrack/nftables-ct.tmpl b/data/templates/conntrack/nftables-ct.tmpl
new file mode 100644
index 000000000..cebc1a54e
--- /dev/null
+++ b/data/templates/conntrack/nftables-ct.tmpl
@@ -0,0 +1,48 @@
+#!/usr/sbin/nft -f
+
+{% set nft_ct_ignore_name = 'VYOS_CT_IGNORE' %}
+{% set nft_ct_timeout_name = 'VYOS_CT_TIMEOUT' %}
+
+# we first flush all chains and render the content from scratch - this makes
+# any delta check obsolete
+flush chain raw {{ nft_ct_ignore_name }}
+flush chain raw {{ nft_ct_timeout_name }}
+
+table raw {
+ chain {{ nft_ct_ignore_name }} {
+{% if ignore.rule is vyos_defined %}
+{% for rule, rule_config in ignore.rule.items() %}
+ # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is defined and rule_config.description is not none }}
+{% set nft_command = '' %}
+{% if rule_config.inbound_interface is vyos_defined %}
+{% set nft_command = nft_command ~ ' iifname ' ~ rule_config.inbound_interface %}
+{% endif %}
+{% if rule_config.protocol is vyos_defined %}
+{% set nft_command = nft_command ~ ' ip protocol ' ~ rule_config.protocol %}
+{% endif %}
+{% if rule_config.destination.address is vyos_defined %}
+{% set nft_command = nft_command ~ ' ip daddr ' ~ rule_config.destination.address %}
+{% endif %}
+{% if rule_config.destination.port is vyos_defined %}
+{% set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' dport { ' ~ rule_config.destination.port ~ ' }' %}
+{% endif %}
+{% if rule_config.source.address is vyos_defined %}
+{% set nft_command = nft_command ~ ' ip saddr ' ~ rule_config.source.address %}
+{% endif %}
+{% if rule_config.source.port is vyos_defined %}
+{% set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' sport { ' ~ rule_config.source.port ~ ' }' %}
+{% endif %}
+ {{ nft_command }} counter notrack comment ignore-{{ rule }}
+{% endfor %}
+{% endif %}
+ return
+ }
+ chain {{ nft_ct_timeout_name }} {
+{% if timeout.custom.rule is vyos_defined %}
+{% for rule, rule_config in timeout.custom.rule.items() %}
+ # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is defined and rule_config.description is not none }}
+{% endfor %}
+{% endif %}
+ return
+ }
+}
diff --git a/data/templates/conntrack/sysctl.conf.tmpl b/data/templates/conntrack/sysctl.conf.tmpl
index 9e97c3286..075402c04 100644
--- a/data/templates/conntrack/sysctl.conf.tmpl
+++ b/data/templates/conntrack/sysctl.conf.tmpl
@@ -6,7 +6,7 @@ net.netfilter.nf_conntrack_max = {{ table_size }}
net.ipv4.tcp_max_syn_backlog = {{ tcp.half_open_connections }}
-net.netfilter.nf_conntrack_tcp_loose = {{ '1' if tcp.loose == 'enable' else '0' }}
+net.netfilter.nf_conntrack_tcp_loose = {{ '1' if tcp.loose is vyos_defined('enable') else '0' }}
net.netfilter.nf_conntrack_tcp_max_retrans = {{ tcp.max_retrans }}
net.netfilter.nf_conntrack_icmp_timeout = {{ timeout.icmp }}
diff --git a/data/templates/containers/registry.tmpl b/data/templates/containers/registry.tmpl
index 0347de673..0cbd9ecc2 100644
--- a/data/templates/containers/registry.tmpl
+++ b/data/templates/containers/registry.tmpl
@@ -1,5 +1,5 @@
### Autogenerated by /usr/libexec/vyos/conf_mode/containers.py ###
-{% if registry is defined and registry is not none %}
+{% if registry is vyos_defined %}
unqualified-search-registries = {{ registry }}
{% endif %}
diff --git a/data/templates/dhcp-client/daemon-options.tmpl b/data/templates/dhcp-client/daemon-options.tmpl
index 40629dca1..5b3bff73f 100644
--- a/data/templates/dhcp-client/daemon-options.tmpl
+++ b/data/templates/dhcp-client/daemon-options.tmpl
@@ -1,4 +1,4 @@
### Autogenerated by interface.py ###
-DHCLIENT_OPTS="-nw -cf /var/lib/dhcp/dhclient_{{ifname}}.conf -pf /var/lib/dhcp/dhclient_{{ifname}}.pid -lf /var/lib/dhcp/dhclient_{{ifname}}.leases{{" -e IF_METRIC=" ~ dhcp_options.default_route_distance if dhcp_options.default_route_distance is defined and dhcp_options.default_route_distance is not none}} {{ifname}}"
+DHCLIENT_OPTS="-nw -cf /var/lib/dhcp/dhclient_{{ ifname }}.conf -pf /var/lib/dhcp/dhclient_{{ ifname }}.pid -lf /var/lib/dhcp/dhclient_{{ ifname }}.leases{{" -e IF_METRIC=" ~ dhcp_options.default_route_distance if dhcp_options.default_route_distance is vyos_defined }} {{ ifname }}"
diff --git a/data/templates/dhcp-client/ipv4.tmpl b/data/templates/dhcp-client/ipv4.tmpl
index b3e74c22b..83fb93dc1 100644
--- a/data/templates/dhcp-client/ipv4.tmpl
+++ b/data/templates/dhcp-client/ipv4.tmpl
@@ -7,7 +7,7 @@ initial-interval 2;
interface "{{ ifname }}" {
send host-name "{{ dhcp_options.host_name }}";
-{% if dhcp_options.client_id is defined and dhcp_options.client_id is not none %}
+{% if dhcp_options.client_id is vyos_defined %}
{% set client_id = dhcp_options.client_id %}
{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. If not HEX, use double quotes ASCII format #}
{% if not dhcp_options.client_id.split(':') | length >= 5 %}
@@ -15,18 +15,18 @@ interface "{{ ifname }}" {
{% endif %}
send dhcp-client-identifier {{ client_id }};
{% endif %}
-{% if dhcp_options.vendor_class_id is defined and dhcp_options.vendor_class_id is not none %}
+{% if dhcp_options.vendor_class_id is vyos_defined %}
send vendor-class-identifier "{{ dhcp_options.vendor_class_id }}";
{% endif %}
# The request statement causes the client to request that any server responding to the
# client send the client its values for the specified options.
- request subnet-mask, broadcast-address,{{ " routers," if dhcp_options.no_default_route is not defined }} domain-name-servers,
+ request subnet-mask, broadcast-address,{{ " routers," if dhcp_options.no_default_route is not vyos_defined }} domain-name-servers,
rfc3442-classless-static-routes, domain-name, interface-mtu;
# The require statement lists options that must be sent in order for an offer to be
# accepted. Offers that do not contain all the listed options will be ignored!
require subnet-mask;
-{% if dhcp_options.reject is defined and dhcp_options.reject is not none %}
+{% if dhcp_options.reject is vyos_defined %}
# Block addresses coming from theses dhcp servers if configured.
reject {{ dhcp_options.reject | join(', ') }};
{% endif %}
diff --git a/data/templates/dhcp-client/ipv6.tmpl b/data/templates/dhcp-client/ipv6.tmpl
index c292664e9..085cfe5a9 100644
--- a/data/templates/dhcp-client/ipv6.tmpl
+++ b/data/templates/dhcp-client/ipv6.tmpl
@@ -2,54 +2,54 @@
# man https://www.unix.com/man-page/debian/5/dhcp6c.conf/
interface {{ ifname }} {
-{% if dhcpv6_options is defined and dhcpv6_options.duid is defined and dhcpv6_options.duid is not none %}
+{% if dhcpv6_options.duid is vyos_defined %}
send client-id {{ dhcpv6_options.duid }};
{% endif %}
-{% if address is defined and 'dhcpv6' in address %}
+{% if address is vyos_defined and 'dhcpv6' in address %}
request domain-name-servers;
request domain-name;
-{% if dhcpv6_options is defined and dhcpv6_options.parameters_only is defined %}
+{% if dhcpv6_options.parameters_only is vyos_defined %}
information-only;
{% endif %}
-{% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %}
+{% if dhcpv6_options.temporary is not vyos_defined %}
send ia-na 0; # non-temporary address
{% endif %}
-{% if dhcpv6_options is defined and dhcpv6_options.rapid_commit is defined %}
+{% if dhcpv6_options.rapid_commit is vyos_defined %}
send rapid-commit; # wait for immediate reply instead of advertisements
{% endif %}
{% endif %}
-{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %}
+{% if dhcpv6_options.pd is vyos_defined %}
{% for pd in dhcpv6_options.pd %}
send ia-pd {{ pd }}; # prefix delegation #{{ pd }}
{% endfor %}
{% endif %}
};
-{% if address is defined and 'dhcpv6' in address %}
-{% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %}
+{% if address is vyos_defined and 'dhcpv6' in address %}
+{% if dhcpv6_options.temporary is not vyos_defined %}
id-assoc na 0 {
# Identity association for non temporary address
};
{% endif %}
{% endif %}
-{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %}
-{% for pd in dhcpv6_options.pd %}
+{% if dhcpv6_options.pd is vyos_defined %}
+{% for pd, pd_config in dhcpv6_options.pd.items() %}
id-assoc pd {{ pd }} {
{# length got a default value #}
- prefix ::/{{ dhcpv6_options.pd[pd].length }} infinity;
-{% set sla_len = 64 - dhcpv6_options.pd[pd].length|int %}
+ prefix ::/{{ pd_config.length }} infinity;
+{% set sla_len = 64 - pd_config.length|int %}
{% set count = namespace(value=0) %}
-{% for interface in dhcpv6_options.pd[pd].interface if dhcpv6_options.pd[pd].interface is defined %}
+{% for interface, interface_config in pd_config.interface.items() if pd_config.interface is vyos_defined %}
prefix-interface {{ interface }} {
sla-len {{ sla_len }};
-{% if dhcpv6_options.pd[pd].interface[interface].sla_id is defined and dhcpv6_options.pd[pd].interface[interface].sla_id is not none %}
- sla-id {{ dhcpv6_options.pd[pd].interface[interface].sla_id }};
+{% if interface_config.sla_id is vyos_defined %}
+ sla-id {{ interface_config.sla_id }};
{% else %}
sla-id {{ count.value }};
{% endif %}
-{% if dhcpv6_options.pd[pd].interface[interface].address is defined and dhcpv6_options.pd[pd].interface[interface].address is not none %}
- ifid {{ dhcpv6_options.pd[pd].interface[interface].address }};
+{% if interface_config.address is vyos_defined %}
+ ifid {{ interface_config.address }};
{% endif %}
};
{% set count.value = count.value + 1 %}
diff --git a/data/templates/dhcp-relay/dhcrelay.conf.tmpl b/data/templates/dhcp-relay/dhcrelay.conf.tmpl
index a9d17ed9a..11710bd8e 100644
--- a/data/templates/dhcp-relay/dhcrelay.conf.tmpl
+++ b/data/templates/dhcp-relay/dhcrelay.conf.tmpl
@@ -1,6 +1,6 @@
### Autogenerated by dhcp_relay.py ###
-{% set max_size = '-A ' + relay_options.max_size if relay_options.max_size is defined and relay_options.max_size is not none %}
+{% set max_size = '-A ' ~ relay_options.max_size if relay_options.max_size is vyos_defined %}
{# hop_count and relay_agents_packets is a default option, thus it is always present #}
OPTIONS="-c {{ relay_options.hop_count }} -a -m {{ relay_options.relay_agents_packets }} {{ max_size }} -i {{ interface | join(' -i ') }} {{ server | join(' ') }}"
diff --git a/data/templates/dhcp-relay/dhcrelay6.conf.tmpl b/data/templates/dhcp-relay/dhcrelay6.conf.tmpl
index 58c216b7c..1fd5de18c 100644
--- a/data/templates/dhcp-relay/dhcrelay6.conf.tmpl
+++ b/data/templates/dhcp-relay/dhcrelay6.conf.tmpl
@@ -4,18 +4,18 @@
{% set upstream = namespace(value='') %}
{% for interface, config in upstream_interface.items() %}
{% for address in config.address %}
-{% set upstream.value = upstream.value + '-u ' + address + '%' + interface + ' ' %}
+{% set upstream.value = upstream.value ~ '-u ' ~ address ~ '%' ~ interface ~ ' ' %}
{% endfor %}
{% endfor %}
{# listen_interface is mandatory so it's always present #}
{% set listen = namespace(value='') %}
{% for interface, config in listen_interface.items() %}
-{% if config.address is defined and config.address is not none %}
-{% set listen.value = listen.value + '-l ' + config.address + '%' + interface + ' ' %}
+{% if config.address is vyos_defined %}
+{% set listen.value = listen.value ~ '-l ' ~ config.address ~ '%' ~ interface ~ ' ' %}
{% else %}
-{% set listen.value = listen.value + '-l ' + interface + ' ' %}
+{% set listen.value = listen.value ~ '-l ' ~ interface ~ ' ' %}
{% endif %}
{% endfor %}
-OPTIONS="{{ listen.value }} {{ upstream.value }} -c {{ max_hop_count }} {{ '-I' if use_interface_id_option is defined }}"
+OPTIONS="{{ listen.value }} {{ upstream.value }} -c {{ max_hop_count }} {{ '-I' if use_interface_id_option is vyos_defined }}"
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl
index e47838c8c..f4297fe5f 100644
--- a/data/templates/dhcp-server/dhcpd.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -4,7 +4,7 @@
# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
#
# log-facility local7;
-{% if hostfile_update is defined %}
+{% if hostfile_update is vyos_defined %}
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);
@@ -17,13 +17,13 @@ on expiry {
}
{% endif %}
-{{ 'use-host-decl-names on;' if host_decl_name is defined }}
-ddns-update-style {{ 'interim' if dynamic_dns_update is defined else 'none' }};
+{{ 'use-host-decl-names on;' if host_decl_name is vyos_defined }}
+ddns-update-style {{ 'interim' if dynamic_dns_update is vyos_defined else 'none' }};
option rfc3442-static-route code 121 = array of integer 8;
option windows-static-route code 249 = array of integer 8;
option wpad-url code 252 = text;
-{% if global_parameters is defined and global_parameters is not none %}
+{% if global_parameters is vyos_defined %}
# The following {{ global_parameters | length }} line(s) have been added as
# global-parameters in the CLI and have not been validated !!!
{% for parameter in global_parameters %}
@@ -31,7 +31,7 @@ option wpad-url code 252 = text;
{% endfor %}
{% endif %}
-{% if failover is defined and failover is not none %}
+{% if failover is vyos_defined %}
# DHCP failover configuration
failover peer "{{ failover.name }}" {
{% if failover.status == 'primary' %}
@@ -42,15 +42,15 @@ failover peer "{{ failover.name }}" {
secondary;
{% endif %}
address {{ failover.source_address }};
- port 520;
+ port 647;
peer address {{ failover.remote }};
- peer port 520;
+ peer port 647;
max-response-delay 30;
max-unacked-updates 10;
load balance max seconds 3;
}
{% endif %}
-{% if listen_address is defined and listen_address is not none %}
+{% if listen_address is vyos_defined %}
# DHCP server serving relay subnet, we need a connector to the real world
{% for address in listen_address %}
@@ -60,70 +60,70 @@ subnet {{ address | network_from_ipv4 }} netmask {{ address | netmask_from_ipv4
{% endif %}
# Shared network configration(s)
-{% 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 shared_network_name is vyos_defined %}
+{% for network, network_config in shared_network_name.items() if network_config.disable is not vyos_defined %}
shared-network {{ network | replace('_','-') }} {
-{% if network_config.authoritative is defined %}
+{% if network_config.authoritative is vyos_defined %}
authoritative;
{% endif %}
-{% if network_config.name_server is defined and network_config.name_server is not none %}
+{% if network_config.name_server is vyos_defined %}
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 %}
+{% if network_config.domain_name is vyos_defined %}
option domain-name "{{ network_config.domain_name }}";
{% endif %}
-{% if network_config.domain_search is defined and network_config.domain_search is not none %}
+{% if network_config.domain_search is vyos_defined %}
option domain-search "{{ network_config.domain_search | join('", "') }}";
{% endif %}
-{% if network_config.ntp_server is defined and network_config.ntp_server is not none %}
+{% if network_config.ntp_server is vyos_defined %}
option ntp-servers {{ network_config.ntp_server | join(', ') }};
{% endif %}
-{% if network_config.ping_check is defined %}
+{% if network_config.ping_check is vyos_defined %}
ping-check true;
{% endif %}
-{% if network_config.shared_network_parameters is defined and network_config.shared_network_parameters is not none %}
+{% if network_config.shared_network_parameters is vyos_defined %}
# The following {{ network_config.shared_network_parameters | length }} line(s)
# were added as shared-network-parameters in the CLI and have not been validated
{% for parameter in network_config.shared_network_parameters %}
{{ parameter }}
{% endfor %}
{% endif %}
-{% if network_config.subnet is defined and network_config.subnet is not none %}
+{% if network_config.subnet is vyos_defined %}
{% for subnet, subnet_config in network_config.subnet.items() %}
-{% if subnet_config.description is defined and subnet_config.description is not none %}
+{% if subnet_config.description is vyos_defined %}
# {{ subnet_config.description }}
{% endif %}
subnet {{ subnet | address_from_cidr }} netmask {{ subnet | netmask_from_cidr }} {
-{% if subnet_config.name_server is defined and subnet_config.name_server is not none %}
+{% if subnet_config.name_server is vyos_defined %}
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 %}
+{% if subnet_config.domain_name is vyos_defined %}
option domain-name "{{ subnet_config.domain_name }}";
{% endif %}
-{% if subnet_config.domain_search is defined and subnet_config.domain_search is not none %}
+{% if subnet_config.domain_search is vyos_defined %}
option domain-search "{{ subnet_config.domain_search | join('", "') }}";
{% endif %}
-{% if subnet_config.ntp_server is defined and subnet_config.ntp_server is not none %}
+{% if subnet_config.ntp_server is vyos_defined %}
option ntp-servers {{ subnet_config.ntp_server | join(', ') }};
{% endif %}
-{% if subnet_config.pop_server is defined and subnet_config.pop_server is not none %}
+{% if subnet_config.pop_server is vyos_defined %}
option pop-server {{ subnet_config.pop_server | join(', ') }};
{% endif %}
-{% if subnet_config.smtp_server is defined and subnet_config.smtp_server is not none %}
+{% if subnet_config.smtp_server is vyos_defined %}
option smtp-server {{ subnet_config.smtp_server | join(', ') }};
{% endif %}
-{% if subnet_config.time_server is defined and subnet_config.time_server is not none %}
+{% if subnet_config.time_server is vyos_defined %}
option time-servers {{ subnet_config.time_server | join(', ') }};
{% endif %}
-{% if subnet_config.wins_server is defined and subnet_config.wins_server is not none %}
+{% if subnet_config.wins_server is vyos_defined %}
option netbios-name-servers {{ subnet_config.wins_server | join(', ') }};
{% endif %}
-{% if subnet_config.static_route is defined and subnet_config.static_route is not none %}
+{% if subnet_config.static_route is vyos_defined %}
{% set static_default_route = '' %}
-{% 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) %}
+{% if subnet_config.default_router is vyos_defined %}
+{% set static_default_route = ', ' ~ '0.0.0.0/0' | isc_static_route(subnet_config.default_router) %}
{% endif %}
-{% if subnet_config.static_route is defined and subnet_config.static_route is not none %}
+{% if subnet_config.static_route is vyos_defined %}
{% 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)) %}
@@ -132,59 +132,59 @@ shared-network {{ network | replace('_','-') }} {
option windows-static-route {{ rfc3442_routes | join(', ') }};
{% endif %}
{% endif %}
-{% if subnet_config.ip_forwarding is defined %}
+{% if subnet_config.ip_forwarding is vyos_defined %}
option ip-forwarding true;
{% endif %}
-{% if subnet_config.default_router and subnet_config.default_router is not none %}
+{% if subnet_config.default_router is vyos_defined %}
option routers {{ subnet_config.default_router }};
{% endif %}
-{% if subnet_config.server_identifier is defined and subnet_config.server_identifier is not none %}
+{% if subnet_config.server_identifier is vyos_defined %}
option dhcp-server-identifier {{ subnet_config.server_identifier }};
{% endif %}
-{% if subnet_config.subnet_parameters is defined and subnet_config.subnet_parameters is not none %}
+{% if subnet_config.subnet_parameters is vyos_defined %}
# The following {{ subnet_config.subnet_parameters | length }} line(s) were added as
# subnet-parameters in the CLI and have not been validated!!!
{% for parameter in subnet_config.subnet_parameters %}
{{ parameter }}
{% endfor %}
{% endif %}
-{% if subnet_config.tftp_server_name is defined and subnet_config.tftp_server_name is not none %}
+{% if subnet_config.tftp_server_name is vyos_defined %}
option tftp-server-name "{{ subnet_config.tftp_server_name }}";
{% endif %}
-{% if subnet_config.bootfile_name is defined and subnet_config.bootfile_name is not none %}
+{% if subnet_config.bootfile_name is vyos_defined %}
option bootfile-name "{{ subnet_config.bootfile_name }}";
filename "{{ subnet_config.bootfile_name }}";
{% endif %}
-{% if subnet_config.bootfile_server is defined and subnet_config.bootfile_server is not none %}
+{% if subnet_config.bootfile_server is vyos_defined %}
next-server {{ subnet_config.bootfile_server }};
{% endif %}
{% if subnet_config.bootfile_size is defined and subnet_config.bootfile_size is not none %}
option boot-size {{ subnet_config.bootfile_size }};
{% endif %}
-{% if subnet_config.time_offset is defined and subnet_config.time_offset is not none %}
+{% if subnet_config.time_offset is vyos_defined %}
option time-offset {{ subnet_config.time_offset }};
{% endif %}
-{% if subnet_config.wpad_url is defined and subnet_config.wpad_url is not none %}
+{% if subnet_config.wpad_url is vyos_defined %}
option wpad-url "{{ subnet_config.wpad_url }}";
{% endif %}
-{% if subnet_config.client_prefix_length is defined and subnet_config.client_prefix_length is not none %}
- option subnet-mask {{ subnet_config.client_prefix_length }};
+{% if subnet_config.client_prefix_length is vyos_defined %}
+ option subnet-mask {{ ('0.0.0.0/' ~ subnet_config.client_prefix_length) | netmask_from_cidr }};
{% endif %}
-{% if subnet_config.lease is defined and subnet_config.lease is not none %}
+{% if subnet_config.lease is vyos_defined %}
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 %}
+{% if network_config.ping_check is not vyos_defined and subnet_config.ping_check is vyos_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('_','-') }} {
-{% if host_config.ip_address is defined and host_config.ip_address is not none %}
+{% if subnet_config.static_mapping is vyos_defined %}
+{% for host, host_config in subnet_config.static_mapping.items() if host_config.disable is not vyos_defined %}
+ host {{ host | replace('_','-') if host_decl_name is vyos_defined else network | replace('_','-') ~ '_' ~ host | replace('_','-') }} {
+{% if host_config.ip_address is vyos_defined %}
fixed-address {{ host_config.ip_address }};
{% endif %}
hardware ethernet {{ host_config.mac_address }};
-{% if host_config.static_mapping_parameters is defined and host_config.static_mapping_parameters is not none %}
+{% if host_config.static_mapping_parameters is vyos_defined %}
# The following {{ host_config.static_mapping_parameters | length }} line(s) were added
# as static-mapping-parameters in the CLI and have not been validated
{% for parameter in host_config.static_mapping_parameters %}
@@ -194,20 +194,20 @@ shared-network {{ network | replace('_','-') }} {
}
{% endfor %}
{% endif %}
-{% if subnet_config.range is defined and subnet_config.range is not none %}
+{% if subnet_config.range is vyos_defined %}
{# pool configuration can only be used if there follows a range option #}
pool {
{% endif %}
-{% if subnet_config.enable_failover is defined %}
+{% if subnet_config.enable_failover is vyos_defined %}
failover peer "{{ failover.name }}";
deny dynamic bootp clients;
{% endif %}
-{% if subnet_config.range is defined and subnet_config.range is not none %}
+{% if subnet_config.range is vyos_defined %}
{% 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 %}
+{% if subnet_config.range is vyos_defined %}
{# pool configuration can only be used if there follows a range option #}
}
{% endif %}
@@ -216,7 +216,7 @@ shared-network {{ network | replace('_','-') }} {
{% endif %}
on commit {
set shared-networkname = "{{ network | replace('_','-') }}";
-{% if hostfile_update is defined %}
+{% if hostfile_update is vyos_defined %}
set ClientIp = binary-to-ascii(10, 8, ".", leased-address);
set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name, "empty_hostname");
diff --git a/data/templates/dhcp-server/dhcpdv6.conf.tmpl b/data/templates/dhcp-server/dhcpdv6.conf.tmpl
index 45d629928..c6a4f4c92 100644
--- a/data/templates/dhcp-server/dhcpdv6.conf.tmpl
+++ b/data/templates/dhcp-server/dhcpdv6.conf.tmpl
@@ -4,74 +4,74 @@
# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
log-facility local7;
-{% if preference is defined and preference is not none %}
+{% if preference is vyos_defined %}
option dhcp6.preference {{ preference }};
{% endif %}
-{% if global_parameters is defined and global_parameters.name_server is defined and global_parameters.name_server is not none %}
+{% if global_parameters.name_server is vyos_defined %}
option dhcp6.name-servers {{ global_parameters.name_server | join(', ') }};
{% endif %}
# Shared network configration(s)
-{% 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 shared_network_name is vyos_defined %}
+{% for network, network_config in shared_network_name.items() if network_config.disable is not vyos_defined %}
shared-network {{ network | replace('_','-') }} {
-{% if network_config.common_options is defined and network_config.common_options is not none %}
-{% if network_config.common_options.info_refresh_time is defined and network_config.common_options.info_refresh_time is not none %}
+{% if network_config.common_options is vyos_defined %}
+{% if network_config.common_options.info_refresh_time is vyos_defined %}
option dhcp6.info-refresh-time {{ network_config.common_options.info_refresh_time }};
{% endif %}
-{% if network_config.common_options.domain_search is defined and network_config.common_options.domain_search is not none %}
+{% if network_config.common_options.domain_search is vyos_defined %}
option dhcp6.domain-search "{{ network_config.common_options.domain_search | join('", "') }}";
{% endif %}
-{% if network_config.common_options.name_server is defined and network_config.common_options.name_server is not none %}
+{% if network_config.common_options.name_server is vyos_defined %}
option dhcp6.name-servers {{ network_config.common_options.name_server | join(', ') }};
{% endif %}
{% endif %}
-{% if network_config.subnet is defined and network_config.subnet is not none %}
+{% if network_config.subnet is vyos_defined %}
{% for subnet, subnet_config in network_config.subnet.items() %}
subnet6 {{ subnet }} {
-{% if subnet_config.address_range is defined and subnet_config.address_range is not none %}
-{% if subnet_config.address_range.prefix is defined and subnet_config.address_range.prefix is not none %}
+{% if subnet_config.address_range is vyos_defined %}
+{% if subnet_config.address_range.prefix is vyos_defined %}
{% for prefix, prefix_config in subnet_config.address_range.prefix.items() %}
- range6 {{ prefix }} {{ "temporary" if prefix_config.temporary is defined }};
+ range6 {{ prefix }} {{ "temporary" if prefix_config.temporary is vyos_defined }};
{% endfor %}
{% endif %}
-{% if subnet_config.address_range.start is defined and subnet_config.address_range.start is not none %}
+{% if subnet_config.address_range.start is vyos_defined %}
{% for address, address_config in subnet_config.address_range.start.items() %}
range6 {{ address }} {{ address_config.stop }};
{% endfor %}
{% endif %}
{% endif %}
-{% if subnet_config.domain_search is defined and subnet_config.domain_search is not none %}
+{% if subnet_config.domain_search is vyos_defined %}
option dhcp6.domain-search "{{ subnet_config.domain_search | join('", "') }}";
{% endif %}
-{% if subnet_config.lease_time is defined and subnet_config.lease_time is not none %}
-{% if subnet_config.lease_time.default is defined and subnet_config.lease_time.default is not none %}
+{% if subnet_config.lease_time is vyos_defined %}
+{% if subnet_config.lease_time.default is vyos_defined %}
default-lease-time {{ subnet_config.lease_time.default }};
{% endif %}
-{% if subnet_config.lease_time.maximum is defined and subnet_config.lease_time.maximum is not none %}
+{% if subnet_config.lease_time.maximum is vyos_defined %}
max-lease-time {{ subnet_config.lease_time.maximum }};
{% endif %}
-{% if subnet_config.lease_time.minimum is defined and subnet_config.lease_time.minimum is not none %}
+{% if subnet_config.lease_time.minimum is vyos_defined %}
min-lease-time {{ subnet_config.lease_time.minimum }};
{% endif %}
{% endif %}
-{% if subnet_config.name_server is defined and subnet_config.name_server is not none %}
+{% if subnet_config.name_server is vyos_defined %}
option dhcp6.name-servers {{ subnet_config.name_server | join(', ') }};
{% endif %}
-{% if subnet_config.nis_domain is defined and subnet_config.nis_domain is not none %}
+{% if subnet_config.nis_domain is vyos_defined %}
option dhcp6.nis-domain-name "{{ subnet_config.nis_domain }}";
{% endif %}
-{% if subnet_config.nis_server is defined and subnet_config.nis_server is not none %}
+{% if subnet_config.nis_server is vyos_defined %}
option dhcp6.nis-servers {{ subnet_config.nis_server | join(', ') }};
{% endif %}
-{% if subnet_config.nisplus_domain is defined and subnet_config.nisplus_domain is not none %}
+{% if subnet_config.nisplus_domain is vyos_defined %}
option dhcp6.nisp-domain-name "{{ subnet_config.nisplus_domain }}";
{% endif %}
-{% if subnet_config.nisplus_server is defined and subnet_config.nisplus_server is not none %}
+{% if subnet_config.nisplus_server is vyos_defined %}
option dhcp6.nisp-servers {{ subnet_config.nisplus_server | join(', ') }};
{% endif %}
-{% if subnet_config.sip_server is defined and subnet_config.sip_server is not none %}
+{% if subnet_config.sip_server is vyos_defined %}
{% set server_ip = [] %}
{% set server_fqdn = [] %}
{% for address in subnet_config.sip_server %}
@@ -81,33 +81,33 @@ shared-network {{ network | replace('_','-') }} {
{% set server_fqdn = server_fqdn.append(address) %}
{% endif %}
{% endfor %}
-{% if server_ip is defined and server_ip | length > 0 %}
+{% if server_ip is vyos_defined and server_ip | length > 0 %}
option dhcp6.sip-servers-addresses {{ server_ip | join(', ') }};
{% endif %}
-{% if server_fqdn is defined and server_fqdn | length > 0 %}
+{% if server_fqdn is vyos_defined and server_fqdn | length > 0 %}
option dhcp6.sip-servers-names "{{ server_fqdn | join('", "') }}";
{% endif %}
{% endif %}
-{% if subnet_config.sntp_server is defined and subnet_config.sntp_server is not none %}
+{% if subnet_config.sntp_server is vyos_defined %}
option dhcp6.sntp-servers {{ subnet_config.sntp_server | join(', ') }};
{% endif %}
-{% if subnet_config.prefix_delegation is defined and subnet_config.prefix_delegation.start is defined and subnet_config.prefix_delegation.start is not none %}
+{% if subnet_config.prefix_delegation.start is vyos_defined %}
{% for prefix, prefix_config in subnet_config.prefix_delegation.start.items() %}
prefix6 {{ prefix }} {{ prefix_config.stop }} /{{ prefix_config.prefix_length }};
{% endfor %}
{% endif %}
-{% if subnet_config.static_mapping is defined and subnet_config.static_mapping is not none %}
+{% if subnet_config.static_mapping is vyos_defined %}
# begin configuration of static client mappings
-{% for host, host_config in subnet_config.static_mapping.items() if host_config.disable is not defined %}
+{% for host, host_config in subnet_config.static_mapping.items() if host_config.disable is not vyos_defined %}
host {{ network | replace('_','-') }}_{{ host | replace('_','-') }} {
-{% if host_config.identifier is defined and host_config.identifier is not none %}
+{% if host_config.identifier is vyos_defined %}
host-identifier option dhcp6.client-id {{ host_config.identifier }};
{% endif %}
-{% if host_config.ipv6_address is defined and host_config.ipv6_address is not none %}
+{% if host_config.ipv6_address is vyos_defined %}
fixed-address6 {{ host_config.ipv6_address }};
{% endif %}
-{% if host_config.ipv6_prefix is defined and host_config.ipv6_prefix is not none %}
+{% if host_config.ipv6_prefix is vyos_defined %}
fixed-prefix6 {{ host_config.ipv6_prefix }};
{% endif %}
}
diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl
index 02efe903b..d4ec80a3a 100644
--- a/data/templates/dns-forwarding/recursor.conf.tmpl
+++ b/data/templates/dns-forwarding/recursor.conf.tmpl
@@ -19,6 +19,9 @@ max-cache-entries={{ cache_size }}
# negative TTL for NXDOMAIN
max-negative-ttl={{ negative_ttl }}
+# timeout
+network-timeout={{ timeout }}
+
# ignore-hosts-file
export-etc-hosts={{ 'no' if ignore_hosts_file is defined else 'yes' }}
diff --git a/data/templates/firewall/nftables-defines.tmpl b/data/templates/firewall/nftables-defines.tmpl
new file mode 100644
index 000000000..d9eb7c199
--- /dev/null
+++ b/data/templates/firewall/nftables-defines.tmpl
@@ -0,0 +1,32 @@
+{% if group is defined %}
+{% if group.address_group is defined %}
+{% for group_name, group_conf in group.address_group.items() %}
+define A_{{ group_name }} = { {{ group_conf.address | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.ipv6_address_group is defined %}
+{% for group_name, group_conf in group.ipv6_address_group.items() %}
+define A6_{{ group_name }} = { {{ group_conf.address | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.mac_group is defined %}
+{% for group_name, group_conf in group.mac_group.items() %}
+define M_{{ group_name }} = { {{ group_conf.mac_address | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.network_group is defined %}
+{% for group_name, group_conf in group.network_group.items() %}
+define N_{{ group_name }} = { {{ group_conf.network | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.ipv6_network_group is defined %}
+{% for group_name, group_conf in group.ipv6_network_group.items() %}
+define N6_{{ group_name }} = { {{ group_conf.network | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.port_group is defined %}
+{% for group_name, group_conf in group.port_group.items() %}
+define P_{{ group_name }} = { {{ group_conf.port | join(",") }} }
+{% endfor %}
+{% endif %}
+{% endif %} \ No newline at end of file
diff --git a/data/templates/firewall/nftables-policy.tmpl b/data/templates/firewall/nftables-policy.tmpl
index aa6bb6fc1..905ffcd09 100644
--- a/data/templates/firewall/nftables-policy.tmpl
+++ b/data/templates/firewall/nftables-policy.tmpl
@@ -1,5 +1,13 @@
#!/usr/sbin/nft -f
+{% if cleanup_commands is defined %}
+{% for command in cleanup_commands %}
+{{ command }}
+{% endfor %}
+{% endif %}
+
+include "/run/nftables_defines.conf"
+
table ip mangle {
{% if first_install is defined %}
chain VYOS_PBR_PREROUTING {
@@ -9,7 +17,7 @@ table ip mangle {
type filter hook postrouting priority -150; policy accept;
}
{% endif %}
-{% if route is defined -%}
+{% if route is defined and route is not none -%}
{% for route_text, conf in route.items() %}
chain VYOS_PBR_{{ route_text }} {
{% if conf.rule is defined %}
@@ -17,11 +25,7 @@ table ip mangle {
{{ rule_conf | nft_rule(route_text, rule_id, 'ip') }}
{% endfor %}
{% endif %}
-{% if conf.default_action is defined %}
- counter {{ conf.default_action | nft_action }} comment "{{ name_text }} default-action {{ conf.default_action }}"
-{% else %}
- counter return
-{% endif %}
+ {{ conf | nft_default_rule(route_text) }}
}
{% endfor %}
{%- endif %}
@@ -36,17 +40,15 @@ table ip6 mangle {
type filter hook postrouting priority -150; policy accept;
}
{% endif %}
-{% if ipv6_route is defined %}
-{% for route_text, conf in ipv6_route.items() %}
+{% if route6 is defined and route6 is not none %}
+{% for route_text, conf in route6.items() %}
chain VYOS_PBR6_{{ route_text }} {
{% if conf.rule is defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not defined %}
{{ rule_conf | nft_rule(route_text, rule_id, 'ip6') }}
{% endfor %}
{% endif %}
-{% if conf.default_action is defined %}
- counter {{ conf.default_action | nft_action }} comment "{{ name_text }} default-action {{ conf.default_action }}"
-{% endif %}
+ {{ conf | nft_default_rule(route_text) }}
}
{% endfor %}
{% endif %}
diff --git a/data/templates/firewall/nftables.tmpl b/data/templates/firewall/nftables.tmpl
index 68e83de64..0cc977cf9 100644
--- a/data/templates/firewall/nftables.tmpl
+++ b/data/templates/firewall/nftables.tmpl
@@ -6,43 +6,7 @@
{% endfor %}
{% endif %}
-{% if group is defined %}
-{% if group.address_group is defined %}
-{% for group_name, group_conf in group.address_group.items() %}
-define A_{{ group_name }} = {
- {{ group_conf.address | join(",") }}
-}
-{% endfor %}
-{% endif %}
-{% if group.ipv6_address_group is defined %}
-{% for group_name, group_conf in group.ipv6_address_group.items() %}
-define A6_{{ group_name }} = {
- {{ group_conf.address | join(",") }}
-}
-{% endfor %}
-{% endif %}
-{% if group.network_group is defined %}
-{% for group_name, group_conf in group.network_group.items() %}
-define N_{{ group_name }} = {
- {{ group_conf.network | join(",") }}
-}
-{% endfor %}
-{% endif %}
-{% if group.ipv6_network_group is defined %}
-{% for group_name, group_conf in group.ipv6_network_group.items() %}
-define N6_{{ group_name }} = {
- {{ group_conf.network | join(",") }}
-}
-{% endfor %}
-{% endif %}
-{% if group.port_group is defined %}
-{% for group_name, group_conf in group.port_group.items() %}
-define P_{{ group_name }} = {
- {{ group_conf.port | join(",") }}
-}
-{% endfor %}
-{% endif %}
-{% endif %}
+include "/run/nftables_defines.conf"
table ip filter {
{% if first_install is defined %}
@@ -67,19 +31,25 @@ table ip filter {
}
{% endif %}
{% if name is defined %}
+{% set ns = namespace(sets=[]) %}
{% for name_text, conf in name.items() %}
-{% set default_log = 'log' if 'enable_default_log' in conf else '' %}
- chain {{ name_text }} {
+ chain NAME_{{ name_text }} {
{% if conf.rule is defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not defined %}
{{ rule_conf | nft_rule(name_text, rule_id) }}
+{% if rule_conf.recent is defined %}
+{% set ns.sets = ns.sets + [name_text + '_' + rule_id] %}
+{% endif %}
{% endfor %}
{% endif %}
-{% if conf.default_action is defined %}
- counter {{ default_log }} {{ conf.default_action | nft_action }} comment "{{ name_text }} default-action {{ conf.default_action }}"
-{% else %}
- return
-{% endif %}
+ {{ conf | nft_default_rule(name_text) }}
+ }
+{% endfor %}
+{% for set_name in ns.sets %}
+ set RECENT_{{ set_name }} {
+ type ipv4_addr
+ size 65535
+ flags dynamic
}
{% endfor %}
{% endif %}
@@ -122,19 +92,25 @@ table ip6 filter {
}
{% endif %}
{% if ipv6_name is defined %}
+{% set ns = namespace(sets=[]) %}
{% for name_text, conf in ipv6_name.items() %}
-{% set default_log = 'log' if 'enable_default_log' in conf else '' %}
- chain {{ name_text }} {
+ chain NAME6_{{ name_text }} {
{% if conf.rule is defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not defined %}
{{ rule_conf | nft_rule(name_text, rule_id, 'ip6') }}
+{% if rule_conf.recent is defined %}
+{% set ns.sets = ns.sets + [name_text + '_' + rule_id] %}
+{% endif %}
{% endfor %}
{% endif %}
-{% if conf.default_action is defined %}
- counter {{ default_log }} {{ conf.default_action | nft_action }} comment "{{ name_text }} default-action {{ conf.default_action }}"
-{% else %}
- return
-{% endif %}
+ {{ conf | nft_default_rule(name_text) }}
+ }
+{% endfor %}
+{% for set_name in ns.sets %}
+ set RECENT6_{{ set_name }} {
+ type ipv6_addr
+ size 65535
+ flags dynamic
}
{% endfor %}
{% endif %}
@@ -211,6 +187,7 @@ table raw {
counter jump VYOS_CT_IGNORE
counter jump VYOS_CT_TIMEOUT
counter jump VYOS_CT_PREROUTING_HOOK
+ counter jump FW_CONNTRACK
notrack
}
@@ -219,6 +196,7 @@ table raw {
counter jump VYOS_CT_IGNORE
counter jump VYOS_CT_TIMEOUT
counter jump VYOS_CT_OUTPUT_HOOK
+ counter jump FW_CONNTRACK
notrack
}
@@ -256,6 +234,10 @@ table raw {
chain VYOS_CT_OUTPUT_HOOK {
return
}
+
+ chain FW_CONNTRACK {
+ accept
+ }
}
table ip6 raw {
@@ -266,12 +248,14 @@ table ip6 raw {
chain PREROUTING {
type filter hook prerouting priority -300; policy accept;
counter jump VYOS_CT_PREROUTING_HOOK
+ counter jump FW_CONNTRACK
notrack
}
chain OUTPUT {
type filter hook output priority -300; policy accept;
counter jump VYOS_CT_OUTPUT_HOOK
+ counter jump FW_CONNTRACK
notrack
}
@@ -282,5 +266,9 @@ table ip6 raw {
chain VYOS_CT_OUTPUT_HOOK {
return
}
+
+ chain FW_CONNTRACK {
+ accept
+ }
}
{% endif %}
diff --git a/data/templates/firewall/upnpd.conf.tmpl b/data/templates/firewall/upnpd.conf.tmpl
new file mode 100644
index 000000000..39cb21373
--- /dev/null
+++ b/data/templates/firewall/upnpd.conf.tmpl
@@ -0,0 +1,172 @@
+# This is the UPNP configuration file
+
+# WAN network interface
+ext_ifname={{ wan_interface }}
+{% if wan_ip is defined %}
+# If the WAN interface has several IP addresses, you
+# can specify the one to use below
+{% for addr in wan_ip %}
+ext_ip={{ addr }}
+{% endfor %}
+{% endif %}
+
+# LAN network interfaces IPs / networks
+{% if listen is defined %}
+# There can be multiple listening IPs for SSDP traffic, in that case
+# use multiple 'listening_ip=...' lines, one for each network interface.
+# It can be IP address or network interface name (ie. "eth0")
+# It is mandatory to use the network interface name in order to enable IPv6
+# HTTP is available on all interfaces.
+# When MULTIPLE_EXTERNAL_IP is enabled, the external IP
+# address associated with the subnet follows. For example:
+# listening_ip=192.168.0.1/24 88.22.44.13
+{% for addr in listen %}
+{% if addr | is_ipv4 %}
+listening_ip={{ addr }}
+{% elif addr | is_ipv6 %}
+ipv6_listening_ip={{ addr }}
+{% else %}
+listening_ip={{ addr }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+# CAUTION: mixing up WAN and LAN interfaces may introduce security risks!
+# Be sure to assign the correct interfaces to LAN and WAN and consider
+# implementing UPnP permission rules at the bottom of this configuration file
+
+# Port for HTTP (descriptions and SOAP) traffic. Set to 0 for autoselect.
+#http_port=0
+# Port for HTTPS. Set to 0 for autoselect (default)
+#https_port=0
+
+# Path to the UNIX socket used to communicate with MiniSSDPd
+# If running, MiniSSDPd will manage M-SEARCH answering.
+# default is /var/run/minissdpd.sock
+#minissdpdsocket=/var/run/minissdpd.sock
+
+{% if nat_pmp is defined %}
+# Enable NAT-PMP support (default is no)
+enable_natpmp=yes
+{% endif %}
+
+# Enable UPNP support (default is yes)
+enable_upnp=yes
+
+{% if pcp_lifetime is defined %}
+# PCP
+# Configure the minimum and maximum lifetime of a port mapping in seconds
+# 120s and 86400s (24h) are suggested values from PCP-base
+{% if pcp_lifetime.max is defined %}
+max_lifetime={{ pcp_lifetime.max }}
+{% endif %}
+{% if pcp_lifetime.min is defined %}
+min_lifetime={{ pcp_lifetime.min }}
+{% endif %}
+{% endif %}
+
+
+# To enable the next few runtime options, see compile time
+# ENABLE_MANUFACTURER_INFO_CONFIGURATION (config.h)
+
+{% if friendly_name is defined %}
+# Name of this service, default is "`uname -s` router"
+friendly_name= {{ friendly_name }}
+{% endif %}
+
+# Manufacturer name, default is "`uname -s`"
+manufacturer_name=VyOS
+
+# Manufacturer URL, default is URL of OS vendor
+manufacturer_url=https://vyos.io/
+
+# Model name, default is "`uname -s` router"
+model_name=VyOS Router Model
+
+# Model description, default is "`uname -s` router"
+model_description=Vyos open source enterprise router/firewall operating system
+
+# Model URL, default is URL of OS vendor
+model_url=https://vyos.io/
+
+{% if secure_mode is defined %}
+# Secure Mode, UPnP clients can only add mappings to their own IP
+secure_mode=yes
+{% else %}
+# Secure Mode, UPnP clients can only add mappings to their own IP
+secure_mode=no
+{% endif %}
+
+{% if presentation_url is defined %}
+# Default presentation URL is HTTP address on port 80
+# If set to an empty string, no presentationURL element will appear
+# in the XML description of the device, which prevents MS Windows
+# from displaying an icon in the "Network Connections" panel.
+#presentation_url= {{ presentation_url }}
+{% endif %}
+
+# Report system uptime instead of daemon uptime
+system_uptime=yes
+
+# Unused rules cleaning.
+# never remove any rule before this threshold for the number
+# of redirections is exceeded. default to 20
+clean_ruleset_threshold=10
+# Clean process work interval in seconds. default to 0 (disabled).
+# a 600 seconds (10 minutes) interval makes sense
+clean_ruleset_interval=600
+
+# Anchor name in pf (default is miniupnpd)
+anchor=VyOS
+
+uuid={{ uuid }}
+
+# Lease file location
+lease_file=/config/upnp.leases
+
+# Daemon's serial and model number when reporting to clients
+# (in XML description)
+#serial=12345678
+#model_number=1
+
+{% if rules is defined %}
+# UPnP permission rules
+# (allow|deny) (external port range) IP/mask (internal port range)
+# A port range is <min port>-<max port> or <port> if there is only
+# one port in the range.
+# IP/mask format must be nnn.nnn.nnn.nnn/nn
+# It is advised to only allow redirection of port >= 1024
+# and end the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
+# The following default ruleset allows specific LAN side IP addresses
+# to request only ephemeral ports. It is recommended that users
+# modify the IP ranges to match their own internal networks, and
+# also consider implementing network-specific restrictions
+# CAUTION: failure to enforce any rules may permit insecure requests to be made!
+{% for rule, config in rules.items() %}
+{% if config.disable is defined %}
+{{ config.action}} {{ config.external_port_range }} {{ config.ip }} {{ config.internal_port_range }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if stun is defined %}
+# WAN interface must have public IP address. Otherwise it is behind NAT
+# and port forwarding is impossible. In some cases WAN interface can be
+# behind unrestricted NAT 1:1 when all incoming traffic is NAT-ed and
+# routed to WAN interfaces without any filtering. In this cases miniupnpd
+# needs to know public IP address and it can be learnt by asking external
+# server via STUN protocol. Following option enable retrieving external
+# public IP address from STUN server and detection of NAT type. You need
+# to specify also external STUN server in stun_host option below.
+# This option is disabled by default.
+ext_perform_stun=yes
+# Specify STUN server, either hostname or IP address
+# Some public STUN servers:
+# stun.stunprotocol.org
+# stun.sipgate.net
+# stun.xten.com
+# stun.l.google.com (on non standard port 19302)
+ext_stun_host={{ stun.host }}
+# Specify STUN UDP port, by default it is standard port 3478.
+ext_stun_port={{ stun.port }}
+{% endif %}
diff --git a/data/templates/frr/bfdd.frr.tmpl b/data/templates/frr/bfdd.frr.tmpl
index 439f79d67..ac55d4634 100644
--- a/data/templates/frr/bfdd.frr.tmpl
+++ b/data/templates/frr/bfdd.frr.tmpl
@@ -1,22 +1,22 @@
-{% if profile is defined or peer is defined %}
+{% if profile is vyos_defined or peer is vyos_defined %}
bfd
-{% if profile is defined and profile is not none %}
+{% if profile is vyos_defined %}
{% for profile_name, profile_config in profile.items() %}
profile {{ profile_name }}
detect-multiplier {{ profile_config.interval.multiplier }}
receive-interval {{ profile_config.interval.receive }}
transmit-interval {{ profile_config.interval.transmit }}
-{% if profile_config.interval.echo_interval is defined and profile_config.interval.echo_interval is not none %}
+{% if profile_config.interval.echo_interval is vyos_defined %}
echo transmit-interval {{ profile_config.interval.echo_interval }}
echo receive-interval {{ profile_config.interval.echo_interval }}
{% endif %}
-{% if profile_config.echo_mode is defined %}
+{% if profile_config.echo_mode is vyos_defined %}
echo-mode
{% endif %}
-{% if profile_config.passive is defined %}
+{% if profile_config.passive is vyos_defined %}
passive-mode
{% endif %}
-{% if profile_config.shutdown is defined %}
+{% if profile_config.shutdown is vyos_defined %}
shutdown
{% else %}
no shutdown
@@ -25,26 +25,26 @@ bfd
!
{% endfor %}
{% endif %}
-{% if peer is defined and peer is not none %}
+{% if peer is vyos_defined %}
{% for peer_name, peer_config in peer.items() %}
- peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }} {{ ' vrf ' + peer_config.vrf if peer_config.vrf is defined and peer_config.vrf is not none }}
+ peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is vyos_defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source.address is vyos_defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source.interface is vyos_defined }} {{ ' vrf ' + peer_config.vrf if peer_config.vrf is vyos_defined }}
detect-multiplier {{ peer_config.interval.multiplier }}
receive-interval {{ peer_config.interval.receive }}
transmit-interval {{ peer_config.interval.transmit }}
-{% if peer_config.interval.echo_interval is defined and peer_config.interval.echo_interval is not none %}
+{% if peer_config.interval.echo_interval is vyos_defined %}
echo transmit-interval {{ peer_config.interval.echo_interval }}
echo receive-interval {{ peer_config.interval.echo_interval }}
{% endif %}
-{% if peer_config.echo_mode is defined %}
+{% if peer_config.echo_mode is vyos_defined %}
echo-mode
{% endif %}
-{% if peer_config.passive is defined %}
+{% if peer_config.passive is vyos_defined %}
passive-mode
{% endif %}
-{% if peer_config.profile is defined and peer_config.profile is not none %}
+{% if peer_config.profile is vyos_defined %}
profile {{ peer_config.profile }}
{% endif %}
-{% if peer_config.shutdown is defined %}
+{% if peer_config.shutdown is vyos_defined %}
shutdown
{% else %}
no shutdown
diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl
index 45e0544b7..8baa128a7 100644
--- a/data/templates/frr/bgpd.frr.tmpl
+++ b/data/templates/frr/bgpd.frr.tmpl
@@ -3,113 +3,113 @@
{% macro bgp_neighbor(neighbor, config, peer_group=false) %}
{% if peer_group == true %}
neighbor {{ neighbor }} peer-group
-{% elif config.peer_group is defined and config.peer_group is not none %}
+{% elif config.peer_group is vyos_defined %}
neighbor {{ neighbor }} peer-group {{ config.peer_group }}
{% endif %}
-{% if config.remote_as is defined and config.remote_as is not none %}
+{% if config.remote_as is vyos_defined %}
neighbor {{ neighbor }} remote-as {{ config.remote_as }}
{% endif %}
-{% if config.interface is defined and config.interface.remote_as is defined and config.interface.remote_as is not none %}
+{% if config.interface.remote_as is vyos_defined %}
neighbor {{ neighbor }} interface remote-as {{ config.interface.remote_as }}
{% endif %}
-{% if config.advertisement_interval is defined and config.advertisement_interval is not none %}
+{% if config.advertisement_interval is vyos_defined %}
neighbor {{ neighbor }} advertisement-interval {{ config.advertisement_interval }}
{% endif %}
-{% if config.bfd is defined %}
+{% if config.bfd is vyos_defined %}
neighbor {{ neighbor }} bfd
-{% if config.bfd.check_control_plane_failure is defined %}
+{% if config.bfd.check_control_plane_failure is vyos_defined %}
neighbor {{ neighbor }} bfd check-control-plane-failure
{% endif %}
-{% if config.bfd.profile is defined and config.bfd.profile is not none %}
+{% if config.bfd.profile is vyos_defined %}
neighbor {{ neighbor }} bfd profile {{ config.bfd.profile }}
{% endif %}
{% endif %}
-{% if config.capability is defined and config.capability is not none %}
-{% if config.capability.dynamic is defined %}
+{% if config.capability is vyos_defined %}
+{% if config.capability.dynamic is vyos_defined %}
neighbor {{ neighbor }} capability dynamic
{% endif %}
-{% if config.capability.extended_nexthop is defined %}
+{% if config.capability.extended_nexthop is vyos_defined %}
neighbor {{ neighbor }} capability extended-nexthop
{% endif %}
{% endif %}
-{% if config.description is defined and config.description is not none %}
+{% if config.description is vyos_defined %}
neighbor {{ neighbor }} description {{ config.description }}
{% endif %}
-{% if config.disable_capability_negotiation is defined %}
+{% if config.disable_capability_negotiation is vyos_defined %}
neighbor {{ neighbor }} dont-capability-negotiate
{% endif %}
-{% if config.ebgp_multihop is defined and config.ebgp_multihop is not none %}
+{% if config.ebgp_multihop is vyos_defined %}
neighbor {{ neighbor }} ebgp-multihop {{ config.ebgp_multihop }}
{% endif %}
-{% if config.graceful_restart is defined and config.graceful_restart is not none %}
-{% if config.graceful_restart == 'enable' %}
+{% if config.graceful_restart is vyos_defined %}
+{% if config.graceful_restart is vyos_defined('enable') %}
{% set graceful_restart = 'graceful-restart' %}
-{% elif config.graceful_restart == 'disable' %}
+{% elif config.graceful_restart is vyos_defined('disable') %}
{% set graceful_restart = 'graceful-restart-disable' %}
-{% elif config.graceful_restart == 'restart-helper' %}
+{% elif config.graceful_restart is vyos_defined('restart-helper') %}
{% set graceful_restart = 'graceful-restart-helper' %}
{% endif %}
neighbor {{ neighbor }} {{ graceful_restart }}
{% endif %}
-{% if config.local_as is defined and config.local_as is not none %}
+{% if config.local_as is vyos_defined %}
{% for local_as, local_as_config in config.local_as.items() %}
{# There can be only one local-as value, this is checked in the Python code #}
- neighbor {{ neighbor }} local-as {{ local_as }} {{ 'no-prepend' if local_as_config.no_prepend is defined }} {{ 'replace-as' if local_as_config.no_prepend is defined and local_as_config.no_prepend.replace_as is defined }}
+ neighbor {{ neighbor }} local-as {{ local_as }} {{ 'no-prepend' if local_as_config.no_prepend is vyos_defined }} {{ 'replace-as' if local_as_config.no_prepend is vyos_defined and local_as_config.no_prepend.replace_as is vyos_defined }}
{% endfor %}
{% endif %}
-{% if config.override_capability is defined %}
+{% if config.override_capability is vyos_defined %}
neighbor {{ neighbor }} override-capability
{% endif %}
-{% if config.passive is defined %}
+{% if config.passive is vyos_defined %}
neighbor {{ neighbor }} passive
{% endif %}
-{% if config.password is defined and config.password is not none %}
+{% if config.password is vyos_defined %}
neighbor {{ neighbor }} password {{ config.password }}
{% endif %}
-{% if config.port is defined and config.port is not none %}
+{% if config.port is vyos_defined %}
neighbor {{ neighbor }} port {{ config.port }}
{% endif %}
-{% if config.shutdown is defined %}
+{% if config.shutdown is vyos_defined %}
neighbor {{ neighbor }} shutdown
{% endif %}
-{% if config.solo is defined %}
+{% if config.solo is vyos_defined %}
neighbor {{ neighbor }} solo
{% endif %}
-{% if config.strict_capability_match is defined %}
+{% if config.strict_capability_match is vyos_defined %}
neighbor {{ neighbor }} strict-capability-match
{% endif %}
-{% if config.ttl_security is defined and config.ttl_security.hops is defined and config.ttl_security.hops is not none %}
+{% if config.ttl_security.hops is vyos_defined %}
neighbor {{ neighbor }} ttl-security hops {{ config.ttl_security.hops }}
{% endif %}
-{% if config.timers is defined %}
-{% if config.timers.connect is defined and config.timers.connect is not none %}
+{% if config.timers is vyos_defined %}
+{% if config.timers.connect is vyos_defined %}
neighbor {{ neighbor }} timers connect {{ config.timers.connect }}
{% endif %}
-{% if config.timers.holdtime is defined and config.timers.keepalive is defined and config.timers.holdtime is not none and config.timers.keepalive is not none %}
+{% if config.timers.keepalive is vyos_defined and config.timers.holdtime is vyos_defined %}
neighbor {{ neighbor }} timers {{ config.timers.keepalive }} {{ config.timers.holdtime }}
{% endif %}
{% endif %}
-{% if config.update_source is defined and config.update_source is not none %}
+{% if config.update_source is vyos_defined %}
neighbor {{ neighbor }} update-source {{ config.update_source }}
{% endif %}
-{% if config.interface is defined and config.interface is not none %}
-{% if config.interface.peer_group is defined and config.interface.peer_group is not none %}
+{% if config.interface is vyos_defined %}
+{% if config.interface.peer_group is vyos_defined %}
neighbor {{ neighbor }} interface peer-group {{ config.interface.peer_group }}
{% endif %}
-{% if config.interface.source_interface is defined and config.interface.source_interface is not none %}
+{% if config.interface.source_interface is vyos_defined %}
neighbor {{ neighbor }} interface {{ config.interface.source_interface }}
{% endif %}
-{% if config.interface.v6only is defined and config.interface.v6only is not none %}
-{% if config.interface.v6only.peer_group is defined and config.interface.v6only.peer_group is not none %}
+{% if config.interface.v6only is vyos_defined %}
+{% if config.interface.v6only.peer_group is vyos_defined %}
neighbor {{ neighbor }} interface v6only peer-group {{ config.interface.v6only.peer_group }}
{% endif %}
-{% if config.interface.v6only.remote_as is defined and config.interface.v6only.remote_as is not none %}
+{% if config.interface.v6only.remote_as is vyos_defined %}
neighbor {{ neighbor }} interface v6only remote-as {{ config.interface.v6only.remote_as }}
{% endif %}
{% endif %}
{% endif %}
!
-{% if config.address_family is defined and config.address_family is not none %}
+{% if config.address_family is vyos_defined %}
{% for afi, afi_config in config.address_family.items() %}
{% if afi == 'ipv4_unicast' %}
address-family ipv4 unicast
@@ -134,104 +134,96 @@
{% elif afi == 'l2vpn_evpn' %}
address-family l2vpn evpn
{% endif %}
-{% if afi_config.addpath_tx_all is defined %}
+{% if afi_config.addpath_tx_all is vyos_defined %}
neighbor {{ neighbor }} addpath-tx-all-paths
{% endif %}
-{% if afi_config.addpath_tx_per_as is defined %}
+{% if afi_config.addpath_tx_per_as is vyos_defined %}
neighbor {{ neighbor }} addpath-tx-bestpath-per-AS
{% endif %}
-{% if afi_config.allowas_in is defined and afi_config.allowas_in is not none %}
- neighbor {{ neighbor }} allowas-in {{ afi_config.allowas_in.number if afi_config.allowas_in.number is defined }}
+{% if afi_config.allowas_in is vyos_defined %}
+ neighbor {{ neighbor }} allowas-in {{ afi_config.allowas_in.number if afi_config.allowas_in.number is vyos_defined }}
{% endif %}
-{% if afi_config.as_override is defined %}
+{% if afi_config.as_override is vyos_defined %}
neighbor {{ neighbor }} as-override
{% endif %}
-{% if afi_config.conditionally_advertise is defined and afi_config.conditionally_advertise is not none %}
-{% if afi_config.conditionally_advertise.advertise_map is defined and afi_config.conditionally_advertise.advertise_map is not none %}
+{% if afi_config.conditionally_advertise is vyos_defined %}
+{% if afi_config.conditionally_advertise.advertise_map is vyos_defined %}
{% set exist_non_exist_map = 'exist-map' %}
-{% if afi_config.conditionally_advertise.exist_map is defined and afi_config.conditionally_advertise.exist_map is not none %}
+{% if afi_config.conditionally_advertise.exist_map is vyos_defined %}
{% set exist_non_exist_map = 'exist-map ' ~ afi_config.conditionally_advertise.exist_map %}
-{% elif afi_config.conditionally_advertise.non_exist_map is defined and afi_config.conditionally_advertise.non_exist_map is not none %}
+{% elif afi_config.conditionally_advertise.non_exist_map is vyos_defined %}
{% set exist_non_exist_map = 'non-exist-map ' ~ afi_config.conditionally_advertise.non_exist_map %}
{% endif %}
neighbor {{ neighbor }} advertise-map {{ afi_config.conditionally_advertise.advertise_map }} {{ exist_non_exist_map }}
{% endif %}
{% endif %}
-{% if afi_config.remove_private_as is defined %}
+{% if afi_config.remove_private_as is vyos_defined %}
neighbor {{ neighbor }} remove-private-AS
{% endif %}
-{% if afi_config.route_reflector_client is defined %}
+{% if afi_config.route_reflector_client is vyos_defined %}
neighbor {{ neighbor }} route-reflector-client
{% endif %}
-{% if afi_config.weight is defined and afi_config.weight is not none %}
+{% if afi_config.weight is vyos_defined %}
neighbor {{ neighbor }} weight {{ afi_config.weight }}
{% endif %}
-{% if afi_config.attribute_unchanged is defined and afi_config.attribute_unchanged is not none %}
- neighbor {{ neighbor }} attribute-unchanged {{ 'as-path ' if afi_config.attribute_unchanged.as_path is defined }}{{ 'med ' if afi_config.attribute_unchanged.med is defined }}{{ 'next-hop ' if afi_config.attribute_unchanged.next_hop is defined }}
+{% if afi_config.attribute_unchanged is vyos_defined %}
+ neighbor {{ neighbor }} attribute-unchanged {{ 'as-path ' if afi_config.attribute_unchanged.as_path is vyos_defined }}{{ 'med ' if afi_config.attribute_unchanged.med is vyos_defined }}{{ 'next-hop ' if afi_config.attribute_unchanged.next_hop is vyos_defined }}
{% endif %}
-{% if afi_config.capability is defined and afi_config.capability.orf is defined and afi_config.capability.orf.prefix_list is defined and afi_config.capability.orf.prefix_list.send is defined %}
+{% if afi_config.capability.orf.prefix_list.send is vyos_defined %}
neighbor {{ neighbor }} capability orf prefix-list send
{% endif %}
-{% if afi_config.capability is defined and afi_config.capability.orf is defined and afi_config.capability.orf.prefix_list is defined and afi_config.capability.orf.prefix_list.receive is defined %}
+{% if afi_config.capability.orf.prefix_list.receive is vyos_defined %}
neighbor {{ neighbor }} capability orf prefix-list receive
{% endif %}
-{% if afi_config.default_originate is defined %}
- neighbor {{ neighbor }} default-originate {{ 'route-map ' ~ afi_config.default_originate.route_map if afi_config.default_originate.route_map is defined }}
+{% if afi_config.default_originate is vyos_defined %}
+ neighbor {{ neighbor }} default-originate {{ 'route-map ' ~ afi_config.default_originate.route_map if afi_config.default_originate.route_map is vyos_defined }}
{% endif %}
-{% if afi_config.distribute_list is defined and afi_config.distribute_list is not none %}
-{% if afi_config.distribute_list.export is defined and afi_config.distribute_list.export is not none %}
+{% if afi_config.distribute_list.export is vyos_defined %}
neighbor {{ neighbor }} distribute-list {{ afi_config.distribute_list.export }} out
-{% endif %}
-{% if afi_config.distribute_list.import is defined and afi_config.distribute_list.import is not none %}
+{% endif %}
+{% if afi_config.distribute_list.import is vyos_defined %}
neighbor {{ neighbor }} distribute-list {{ afi_config.distribute_list.import }} in
-{% endif %}
{% endif %}
-{% if afi_config.filter_list is defined and afi_config.filter_list is not none %}
-{% if afi_config.filter_list.export is defined and afi_config.filter_list.export is not none %}
+{% if afi_config.filter_list.export is vyos_defined %}
neighbor {{ neighbor }} filter-list {{ afi_config.filter_list.export }} out
-{% endif %}
-{% if afi_config.filter_list.import is defined and afi_config.filter_list.import is not none %}
+{% endif %}
+{% if afi_config.filter_list.import is vyos_defined %}
neighbor {{ neighbor }} filter-list {{ afi_config.filter_list.import }} in
-{% endif %}
{% endif %}
-{% if afi_config.maximum_prefix is defined and afi_config.maximum_prefix is not none %}
+{% if afi_config.maximum_prefix is vyos_defined %}
neighbor {{ neighbor }} maximum-prefix {{ afi_config.maximum_prefix }}
{% endif %}
-{% if afi_config.maximum_prefix_out is defined and afi_config.maximum_prefix_out is not none %}
+{% if afi_config.maximum_prefix_out is vyos_defined %}
neighbor {{ neighbor }} maximum-prefix-out {{ afi_config.maximum_prefix_out }}
{% endif %}
-{% if afi_config.nexthop_self is defined %}
- neighbor {{ neighbor }} next-hop-self {{ 'force' if afi_config.nexthop_self.force is defined }}
+{% if afi_config.nexthop_self is vyos_defined %}
+ neighbor {{ neighbor }} next-hop-self {{ 'force' if afi_config.nexthop_self.force is vyos_defined }}
{% endif %}
-{% if afi_config.route_server_client is defined %}
+{% if afi_config.route_server_client is vyos_defined %}
neighbor {{ neighbor }} route-server-client
{% endif %}
-{% if afi_config.route_map is defined and afi_config.route_map is not none %}
-{% if afi_config.route_map.export is defined and afi_config.route_map.export is not none %}
+{% if afi_config.route_map.export is vyos_defined %}
neighbor {{ neighbor }} route-map {{ afi_config.route_map.export }} out
-{% endif %}
-{% if afi_config.route_map.import is defined and afi_config.route_map.import is not none %}
+{% endif %}
+{% if afi_config.route_map.import is vyos_defined %}
neighbor {{ neighbor }} route-map {{ afi_config.route_map.import }} in
-{% endif %}
{% endif %}
-{% if afi_config.prefix_list is defined and afi_config.prefix_list is not none %}
-{% if afi_config.prefix_list.export is defined and afi_config.prefix_list.export is not none %}
+{% if afi_config.prefix_list.export is vyos_defined %}
neighbor {{ neighbor }} prefix-list {{ afi_config.prefix_list.export }} out
-{% endif %}
-{% if afi_config.prefix_list.import is defined and afi_config.prefix_list.import is not none %}
+{% endif %}
+{% if afi_config.prefix_list.import is vyos_defined %}
neighbor {{ neighbor }} prefix-list {{ afi_config.prefix_list.import }} in
-{% endif %}
{% endif %}
-{% if afi_config.soft_reconfiguration is defined and afi_config.soft_reconfiguration.inbound is defined %}
+{% if afi_config.soft_reconfiguration.inbound is vyos_defined %}
neighbor {{ neighbor }} soft-reconfiguration inbound
{% endif %}
-{% if afi_config.unsuppress_map is defined and afi_config.unsuppress_map is not none %}
+{% if afi_config.unsuppress_map is vyos_defined %}
neighbor {{ neighbor }} unsuppress-map {{ afi_config.unsuppress_map }}
{% endif %}
-{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.extended is defined %}
+{% if afi_config.disable_send_community.extended is vyos_defined %}
no neighbor {{ neighbor }} send-community extended
{% endif %}
-{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.standard is defined %}
+{% if afi_config.disable_send_community.standard is vyos_defined %}
no neighbor {{ neighbor }} send-community standard
{% endif %}
neighbor {{ neighbor }} activate
@@ -241,8 +233,8 @@
{% endif %}
{% endmacro %}
!
-router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none }}
-{% if parameters is defined and parameters.ebgp_requires_policy is defined %}
+router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
+{% if parameters.ebgp_requires_policy is vyos_defined %}
bgp ebgp-requires-policy
{% else %}
no bgp ebgp-requires-policy
@@ -251,7 +243,7 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
no bgp default ipv4-unicast
{# Workaround for T2100 until we have decided about a migration script #}
no bgp network import-check
-{% if address_family is defined and address_family is not none %}
+{% if address_family is vyos_defined %}
{% for afi, afi_config in address_family.items() %}
!
{% if afi == 'ipv4_unicast' %}
@@ -276,25 +268,25 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
address-family ipv6 flowspec
{% elif afi == 'l2vpn_evpn' %}
address-family l2vpn evpn
-{% if afi_config.rd is defined and afi_config.rd is not none %}
+{% if afi_config.rd is vyos_defined %}
rd {{ afi_config.rd }}
{% endif %}
{% endif %}
-{% if afi_config.aggregate_address is defined and afi_config.aggregate_address is not none %}
+{% if afi_config.aggregate_address is vyos_defined %}
{% for aggregate, aggregate_config in afi_config.aggregate_address.items() %}
- aggregate-address {{ aggregate }}{{ ' as-set' if aggregate_config.as_set is defined }}{{ ' summary-only' if aggregate_config.summary_only is defined }}
-{% if aggregate_config.route_map is defined and aggregate_config.route_map is not none %}
+ aggregate-address {{ aggregate }}{{ ' as-set' if aggregate_config.as_set is vyos_defined }}{{ ' summary-only' if aggregate_config.summary_only is vyos_defined }}
+{% if aggregate_config.route_map is vyos_defined %}
aggregate-address {{ aggregate }} route-map {{ aggregate_config.route_map }}
{% endif %}
{% endfor %}
{% endif %}
-{% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ebgp is defined and afi_config.maximum_paths.ebgp is not none %}
+{% if afi_config.maximum_paths.ebgp is vyos_defined %}
maximum-paths {{ afi_config.maximum_paths.ebgp }}
{% endif %}
-{% if afi_config.maximum_paths is defined and afi_config.maximum_paths.ibgp is defined and afi_config.maximum_paths.ibgp is not none %}
+{% if afi_config.maximum_paths.ibgp is vyos_defined %}
maximum-paths ibgp {{ afi_config.maximum_paths.ibgp }}
{% endif %}
-{% if afi_config.redistribute is defined and afi_config.redistribute is not none %}
+{% if afi_config.redistribute is vyos_defined %}
{% for protocol in afi_config.redistribute %}
{% if protocol == 'table' %}
redistribute table {{ afi_config.redistribute[protocol].table }}
@@ -303,135 +295,123 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% if protocol == 'ospfv3' %}
{% set redistribution_protocol = 'ospf6' %}
{% endif %}
- redistribute {{ redistribution_protocol }}{% if afi_config.redistribute[protocol].metric is defined %} metric {{ afi_config.redistribute[protocol].metric }}{% endif %}{% if afi_config.redistribute[protocol].route_map is defined %} route-map {{ afi_config.redistribute[protocol].route_map }}{% endif %}
+ redistribute {{ redistribution_protocol }}{% if afi_config.redistribute[protocol].metric is vyos_defined %} metric {{ afi_config.redistribute[protocol].metric }}{% endif %}{% if afi_config.redistribute[protocol].route_map is vyos_defined %} route-map {{ afi_config.redistribute[protocol].route_map }}{% endif %}
{####### we need this blank line!! #######}
{% endif %}
{% endfor %}
{% endif %}
-{% if afi_config.network is defined and afi_config.network is not none %}
+{% if afi_config.network is vyos_defined %}
{% for network in afi_config.network %}
- network {{ network }}{% if afi_config.network[network].route_map is defined %} route-map {{ afi_config.network[network].route_map }}{% endif %}{% if afi_config.network[network].backdoor is defined %} backdoor{% endif %}{% if afi_config.network[network].rd is defined and afi_config.network[network].label is defined%} rd {{ afi_config.network[network].rd }} label {{ afi_config.network[network].label }}{% endif %}
+ network {{ network }}{% if afi_config.network[network].route_map is vyos_defined %} route-map {{ afi_config.network[network].route_map }}{% endif %}{% if afi_config.network[network].backdoor is vyos_defined %} backdoor{% endif %}{% if afi_config.network[network].rd is vyos_defined and afi_config.network[network].label is vyos_defined %} rd {{ afi_config.network[network].rd }} label {{ afi_config.network[network].label }}{% endif %}
{####### we need this blank line!! #######}
{% endfor %}
{% endif %}
-{% if afi_config.advertise is defined and afi_config.advertise is not none %}
+{% if afi_config.advertise is vyos_defined %}
{% for adv_afi, adv_afi_config in afi_config.advertise.items() %}
-{% if adv_afi_config.unicast is defined and adv_afi_config.unicast is not none %}
- advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is defined }}
+{% if adv_afi_config.unicast is vyos_defined %}
+ advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is vyos_defined }}
{% endif %}
{% endfor %}
{% endif %}
-{% if afi_config.distance is defined and afi_config.distance is not none %}
-{% if afi_config.distance is defined and afi_config.distance.external is defined and afi_config.distance.internal is defined and afi_config.distance.local is defined %}
+{% if afi_config.distance.external is vyos_defined and afi_config.distance.internal is vyos_defined and afi_config.distance.local is vyos_defined %}
distance bgp {{ afi_config.distance.external }} {{ afi_config.distance.internal }} {{ afi_config.distance.local }}
-{% endif %}
-{% if afi_config.distance.prefix is defined and afi_config.distance.prefix is not none %}
-{% for prefix in afi_config.distance.prefix %}
+{% endif %}
+{% if afi_config.distance.prefix is vyos_defined %}
+{% for prefix in afi_config.distance.prefix %}
distance {{ afi_config.distance.prefix[prefix].distance }} {{ prefix }}
-{% endfor %}
-{% endif %}
+{% endfor %}
{% endif %}
-{% if afi_config.export is defined and afi_config.export.vpn is defined %}
+{% if afi_config.export.vpn is vyos_defined %}
export vpn
{% endif %}
-{% if afi_config.import is defined and afi_config.import is not none %}
-{% if afi_config.import.vpn is defined %}
+{% if afi_config.import.vpn is vyos_defined %}
import vpn
-{% endif %}
-{% if afi_config.import.vrf is defined and afi_config.import.vrf is not none %}
-{% for vrf in afi_config.import.vrf %}
+{% endif %}
+{% if afi_config.import.vrf is vyos_defined %}
+{% for vrf in afi_config.import.vrf %}
import vrf {{ vrf }}
-{% endfor %}
-{% endif %}
+{% endfor %}
{% endif %}
-{% if afi_config.label is defined and afi_config.label.vpn is defined and afi_config.label.vpn.export is defined and afi_config.label.vpn.export is not none %}
+{% if afi_config.label.vpn.export is vyos_defined %}
label vpn export {{ afi_config.label.vpn.export }}
{% endif %}
-{% if afi_config.local_install is defined and afi_config.local_install is not none %}
+{% if afi_config.local_install is vyos_defined %}
{% for interface in afi_config.local_install.interface %}
local-install {{ interface }}
{% endfor %}
{% endif %}
-{% if afi_config.advertise_all_vni is defined %}
+{% if afi_config.advertise_all_vni is vyos_defined %}
advertise-all-vni
{% endif %}
-{% if afi_config.advertise_default_gw is defined %}
+{% if afi_config.advertise_default_gw is vyos_defined %}
advertise-default-gw
{% endif %}
-{% if afi_config.advertise_pip is defined and afi_config.advertise_pip is not none %}
+{% if afi_config.advertise_pip is vyos_defined %}
advertise-pip ip {{ afi_config.advertise_pip }}
{% endif %}
-{% if afi_config.advertise_svi_ip is defined %}
+{% if afi_config.advertise_svi_ip is vyos_defined %}
advertise-svi-ip
{% endif %}
-{% if afi_config.rt_auto_derive is defined %}
+{% if afi_config.rt_auto_derive is vyos_defined %}
autort rfc8365-compatible
{% endif %}
-{% if afi_config.flooding is defined and afi_config.flooding.disable is defined %}
+{% if afi_config.flooding.disable is vyos_defined %}
flooding disable
{% endif %}
-{% if afi_config.flooding is defined and afi_config.flooding.head_end_replication is defined %}
+{% if afi_config.flooding.head_end_replication is vyos_defined %}
flooding head-end-replication
{% endif %}
-{% if afi_config.rd is defined and afi_config.rd.vpn is defined and afi_config.rd.vpn.export is defined %}
+{% if afi_config.rd.vpn.export is vyos_defined %}
rd vpn export {{ afi_config.rd.vpn.export }}
{% endif %}
-{% if afi_config.route_target is defined and afi_config.route_target is not none %}
-{% if afi_config.route_target.vpn is defined and afi_config.route_target.vpn is not none %}
-{% if afi_config.route_target.vpn.both is defined and afi_config.route_target.vpn.both is not none %}
+{% if afi_config.route_target.vpn.both is vyos_defined %}
route-target vpn both {{ afi_config.route_target.vpn.both }}
-{% else %}
-{% if afi_config.route_target.vpn.export is defined and afi_config.route_target.vpn.export is not none %}
+{% else %}
+{% if afi_config.route_target.vpn.export is vyos_defined %}
route-target vpn export {{ afi_config.route_target.vpn.export }}
-{% endif %}
-{% if afi_config.route_target.vpn.import is defined and afi_config.route_target.vpn.import is not none %}
+{% endif %}
+{% if afi_config.route_target.vpn.import is vyos_defined %}
route-target vpn import {{ afi_config.route_target.vpn.import }}
-{% endif %}
-{% endif %}
{% endif %}
-{% if afi_config.route_target.both is defined and afi_config.route_target.both is not none %}
+{% endif %}
+{% if afi_config.route_target.both is vyos_defined %}
route-target both {{ afi_config.route_target.both }}
-{% else %}
-{% if afi_config.route_target.export is defined and afi_config.route_target.export is not none %}
+{% else %}
+{% if afi_config.route_target.export is vyos_defined %}
route-target export {{ afi_config.route_target.export }}
-{% endif %}
-{% if afi_config.route_target.import is defined and afi_config.route_target.import is not none %}
+{% endif %}
+{% if afi_config.route_target.import is vyos_defined %}
route-target import {{ afi_config.route_target.import }}
-{% endif %}
{% endif %}
{% endif %}
-{% if afi_config.route_map is defined and afi_config.route_map.vpn is defined and afi_config.route_map.vpn is not none %}
-{% if afi_config.route_map.vpn.export is defined and afi_config.route_map.vpn.export is not none %}
+{% if afi_config.route_map.vpn.export is vyos_defined %}
route-map vpn export {{ afi_config.route_map.vpn.export }}
-{% endif %}
-{% if afi_config.route_map.vpn.import is defined and afi_config.route_map.vpn.import is not none %}
+{% endif %}
+{% if afi_config.route_map.vpn.import is vyos_defined %}
route-map vpn import {{ afi_config.route_map.vpn.import }}
-{% endif %}
{% endif %}
-{% if afi_config.vni is defined and afi_config.vni is not none %}
+{% if afi_config.vni is vyos_defined %}
{% for vni, vni_config in afi_config.vni.items() %}
vni {{ vni }}
-{% if vni_config.advertise_default_gw is defined %}
+{% if vni_config.advertise_default_gw is vyos_defined %}
advertise-default-gw
{% endif %}
-{% if vni_config.advertise_svi_ip is defined %}
+{% if vni_config.advertise_svi_ip is vyos_defined %}
advertise-svi-ip
{% endif %}
-{% if vni_config.rd is defined and vni_config.rd is not none %}
+{% if vni_config.rd is vyos_defined %}
rd {{ vni_config.rd }}
{% endif %}
-{% if vni_config.route_target is defined and vni_config.route_target is not none %}
-{% if vni_config.route_target.both is defined and vni_config.route_target.both is not none %}
+{% if vni_config.route_target.both is vyos_defined %}
route-target both {{ vni_config.route_target.both }}
-{% endif %}
-{% if vni_config.route_target.export is defined and vni_config.route_target.export is not none %}
+{% endif %}
+{% if vni_config.route_target.export is vyos_defined %}
route-target export {{ vni_config.route_target.export }}
-{% endif %}
-{% if vni_config.route_target.import is defined and vni_config.route_target.import is not none %}
+{% endif %}
+{% if vni_config.route_target.import is vyos_defined %}
route-target import {{ vni_config.route_target.import }}
-{% endif %}
{% endif %}
exit-vni
{% endfor %}
@@ -440,125 +420,116 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% endfor %}
{% endif %}
!
-{% if peer_group is defined and peer_group is not none %}
+{% if peer_group is vyos_defined %}
{% for peer, config in peer_group.items() %}
{{ bgp_neighbor(peer, config, true) }}
{% endfor %}
{% endif %}
!
-{% if neighbor is defined and neighbor is not none %}
+{% if neighbor is vyos_defined %}
{% for peer, config in neighbor.items() %}
{{ bgp_neighbor(peer, config) }}
{% endfor %}
{% endif %}
!
-{% if listen is defined %}
-{% if listen.limit is defined and listen.limit is not none %}
+{% if listen.limit is vyos_defined %}
bgp listen limit {{ listen.limit }}
-{% endif %}
+{% endif %}
+{% if listen.range is vyos_defined %}
{% for prefix, options in listen.range.items() %}
-{% if options.peer_group is defined and options.peer_group is not none %}
+{% if options.peer_group is vyos_defined %}
bgp listen range {{ prefix }} peer-group {{ options.peer_group }}
{% endif %}
{% endfor %}
{% endif %}
-{% if parameters is defined %}
-{% if parameters.always_compare_med is defined %}
+{% if parameters.always_compare_med is vyos_defined %}
bgp always-compare-med
-{% endif %}
-{% if parameters.bestpath is defined and parameters.bestpath is not none %}
-{% if parameters.bestpath.as_path is defined and parameters.bestpath.as_path is not none %}
-{% for option in parameters.bestpath.as_path %}
+{% endif %}
+{% if parameters.bestpath.as_path is vyos_defined %}
+{% for option in parameters.bestpath.as_path %}
{# replace is required for multipath-relax option #}
bgp bestpath as-path {{ option|replace('_', '-') }}
-{% endfor %}
-{% endif %}
-{% if parameters.bestpath.bandwidth is defined and parameters.bestpath.bandwidth is not none %}
+{% endfor %}
+{% endif %}
+{% if parameters.bestpath.bandwidth is vyos_defined %}
bgp bestpath bandwidth {{ parameters.bestpath.bandwidth }}
-{% endif %}
-{% if parameters.bestpath.compare_routerid is defined %}
+{% endif %}
+{% if parameters.bestpath.compare_routerid is vyos_defined %}
bgp bestpath compare-routerid
-{% endif %}
-{% if parameters.bestpath.med is defined and parameters.bestpath.med is not none %}
- bgp bestpath med {{ 'confed' if parameters.bestpath.med.confed is defined }} {{ 'missing-as-worst' if parameters.bestpath.med.missing_as_worst is defined }}
-{% endif %}
-{% endif %}
-{% if parameters.cluster_id is defined and parameters.cluster_id is not none %}
+{% endif %}
+{% if parameters.bestpath.med is vyos_defined %}
+ bgp bestpath med {{ 'confed' if parameters.bestpath.med.confed is vyos_defined }} {{ 'missing-as-worst' if parameters.bestpath.med.missing_as_worst is vyos_defined }}
+{% endif %}
+{% if parameters.cluster_id is vyos_defined %}
bgp cluster-id {{ parameters.cluster_id }}
-{% endif %}
-{% if parameters.conditional_advertisement is defined and parameters.conditional_advertisement is not none %}
-{% if parameters.conditional_advertisement.timer is defined and parameters.conditional_advertisement.timer is not none %}
+{% endif %}
+{% if parameters.conditional_advertisement.timer is vyos_defined %}
bgp conditional-advertisement timer {{ parameters.conditional_advertisement.timer }}
-{% endif %}
-{% endif %}
-{% if parameters.confederation is defined and parameters.confederation is not none %}
-{% if parameters.confederation.identifier is defined and parameters.confederation.identifier is not none %}
+{% endif %}
+{% if parameters.confederation.identifier is vyos_defined %}
bgp confederation identifier {{ parameters.confederation.identifier }}
-{% endif %}
-{% if parameters.confederation.peers is defined and parameters.confederation.peers is not none %}
+{% endif %}
+{% if parameters.confederation.peers is vyos_defined %}
bgp confederation peers {{ parameters.confederation.peers | join(' ') }}
-{% endif %}
-{% endif %}
-{% if parameters.dampening is defined and parameters.dampening is defined and parameters.dampening.half_life is defined and parameters.dampening.half_life is not none %}
+{% endif %}
+{% if parameters.dampening.half_life is vyos_defined %}
{# Doesn't work in current FRR configuration; vtysh (bgp dampening 16 751 2001 61) #}
- bgp dampening {{ parameters.dampening.half_life }} {{ parameters.dampening.re_use if parameters.dampening.re_use is defined }} {{ parameters.dampening.start_suppress_time if parameters.dampening.start_suppress_time is defined }} {{ parameters.dampening.max_suppress_time if parameters.dampening.max_suppress_time is defined }}
-{% endif %}
-{% if parameters.default is defined and parameters.default is not none %}
-{% if parameters.default.local_pref is defined and parameters.default.local_pref is not none %}
+ bgp dampening {{ parameters.dampening.half_life }} {{ parameters.dampening.re_use if parameters.dampening.re_use is vyos_defined }} {{ parameters.dampening.start_suppress_time if parameters.dampening.start_suppress_time is vyos_defined }} {{ parameters.dampening.max_suppress_time if parameters.dampening.max_suppress_time is vyos_defined }}
+{% endif %}
+{% if parameters.default.local_pref is vyos_defined %}
bgp default local-preference {{ parameters.default.local_pref }}
-{% endif %}
-{% endif %}
-{% if parameters.deterministic_med is defined %}
+{% endif %}
+{% if parameters.deterministic_med is vyos_defined %}
bgp deterministic-med
-{% endif %}
-{% if parameters.distance is defined and parameters.distance is not none %}
-{% if parameters.distance.global is defined and parameters.distance.global.external is defined and parameters.distance.global.internal is defined and parameters.distance.global.local is defined %}
+{% endif %}
+{% if parameters.distance.global.external is vyos_defined and parameters.distance.global.internal is vyos_defined and parameters.distance.global.local is vyos_defined %}
distance bgp {{ parameters.distance.global.external }} {{ parameters.distance.global.internal }} {{ parameters.distance.global.local }}
-{% endif %}
-{% if parameters.distance.prefix is defined and parameters.distance.prefix is not none %}
-{% for prefix in parameters.distance.prefix %}
+{% endif %}
+{% if parameters.distance.prefix is vyos_defined %}
+{% for prefix in parameters.distance.prefix %}
distance {{ parameters.distance.prefix[prefix].distance }} {{ prefix }}
-{% endfor %}
-{% endif %}
-{% endif %}
-{% if parameters.fast_convergence is defined %}
+{% endfor %}
+{% endif %}
+{% if parameters.fast_convergence is vyos_defined %}
bgp fast-convergence
-{% endif %}
-{% if parameters.graceful_restart is defined %}
- bgp graceful-restart {{ 'stalepath-time ' ~ parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is defined }}
-{% endif %}
-{% if parameters.graceful_shutdown is defined %}
+{% endif %}
+{% if parameters.graceful_restart is vyos_defined %}
+ bgp graceful-restart {{ 'stalepath-time ' ~ parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is vyos_defined }}
+{% endif %}
+{% if parameters.graceful_shutdown is vyos_defined %}
bgp graceful-shutdown
-{% endif %}
-{% if parameters.log_neighbor_changes is defined %}
+{% endif %}
+{% if parameters.log_neighbor_changes is vyos_defined %}
bgp log-neighbor-changes
-{% endif %}
-{% if parameters.minimum_holdtime is defined and parameters.minimum_holdtime is not none %}
+{% endif %}
+{% if parameters.minimum_holdtime is vyos_defined %}
bgp minimum-holdtime {{ parameters.minimum_holdtime }}
-{% endif %}
-{% if parameters.network_import_check is defined %}
+{% endif %}
+{% if parameters.network_import_check is vyos_defined %}
bgp network import-check
-{% endif %}
-{% if parameters.no_client_to_client_reflection is defined %}
+{% endif %}
+{% if parameters.no_client_to_client_reflection is vyos_defined %}
no bgp client-to-client reflection
-{% endif %}
-{% if parameters.no_fast_external_failover is defined %}
+{% endif %}
+{% if parameters.no_fast_external_failover is vyos_defined %}
no bgp fast-external-failover
-{% endif %}
-{% if parameters.reject_as_sets is defined %}
+{% endif %}
+{% if parameters.no_suppress_duplicates is vyos_defined %}
+ no bgp suppress-duplicates
+{% endif %}
+{% if parameters.reject_as_sets is vyos_defined %}
bgp reject-as-sets
-{% endif %}
-{% if parameters.router_id is defined and parameters.router_id is not none %}
+{% endif %}
+{% if parameters.router_id is vyos_defined and parameters.router_id is not none %}
bgp router-id {{ parameters.router_id }}
-{% endif %}
-{% if parameters.shutdown is defined %}
+{% endif %}
+{% if parameters.shutdown is vyos_defined %}
bgp shutdown
-{% endif %}
-{% if parameters.suppress_fib_pending is defined %}
+{% endif %}
+{% if parameters.suppress_fib_pending is vyos_defined %}
bgp suppress-fib-pending
-{% endif %}
{% endif %}
-{% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %}
+{% if timers.keepalive is vyos_defined and timers.holdtime is vyos_defined %}
timers bgp {{ timers.keepalive }} {{ timers.holdtime }}
{% endif %}
exit
diff --git a/data/templates/frr/isisd.frr.tmpl b/data/templates/frr/isisd.frr.tmpl
index b1e3f825b..238541903 100644
--- a/data/templates/frr/isisd.frr.tmpl
+++ b/data/templates/frr/isisd.frr.tmpl
@@ -1,46 +1,48 @@
!
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
-interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
+interface {{ iface }}
ip router isis VyOS
ipv6 router isis VyOS
-{% if iface_config.bfd is defined %}
+{% if iface_config.bfd is vyos_defined %}
isis bfd
-{% if iface_config.bfd.profile is defined and iface_config.bfd.profile is not none %}
+{% if iface_config.bfd.profile is vyos_defined %}
isis bfd profile {{ iface_config.bfd.profile }}
{% endif %}
{% endif %}
-{% if iface_config.network is defined and iface_config.network.point_to_point is defined %}
+{% if iface_config.network.point_to_point is vyos_defined %}
isis network point-to-point
{% endif %}
-{% if iface_config.circuit_type is defined %}
+{% if iface_config.circuit_type is vyos_defined %}
isis circuit-type {{ iface_config.circuit_type }}
{% endif %}
-{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %}
+{% if iface_config.hello_interval is vyos_defined %}
isis hello-interval {{ iface_config.hello_interval }}
{% endif %}
-{% if iface_config.hello_multiplier is defined and iface_config.hello_multiplier is not none %}
+{% if iface_config.hello_multiplier is vyos_defined %}
isis hello-multiplier {{ iface_config.hello_multiplier }}
{% endif %}
-{% if iface_config.hello_padding is defined %}
+{% if iface_config.hello_padding is vyos_defined %}
isis hello padding
{% endif %}
-{% if iface_config.metric is defined and iface_config.metric is not none %}
+{% if iface_config.metric is vyos_defined %}
isis metric {{ iface_config.metric }}
{% endif %}
-{% if iface_config.passive is defined %}
+{% if iface_config.passive is vyos_defined %}
isis passive
{% endif %}
-{% if iface_config.password is defined and iface_config.password.plaintext_password is defined and iface_config.password.plaintext_password is not none %}
+{% if iface_config.password.md5 is vyos_defined %}
+ isis password md5 {{ iface_config.password.md5 }}
+{% elif iface_config.password.plaintext_password is vyos_defined %}
isis password clear {{ iface_config.password.plaintext_password }}
{% endif %}
-{% if iface_config.priority is defined and iface_config.priority is not none %}
+{% if iface_config.priority is vyos_defined %}
isis priority {{ iface_config.priority }}
{% endif %}
-{% if iface_config.psnp_interval is defined and iface_config.psnp_interval is not none %}
+{% if iface_config.psnp_interval is vyos_defined %}
isis psnp-interval {{ iface_config.psnp_interval }}
{% endif %}
-{% if iface_config.no_three_way_handshake is defined %}
+{% if iface_config.no_three_way_handshake is vyos_defined %}
no isis three-way-handshake
{% endif %}
exit
@@ -48,98 +50,95 @@ exit
{% endfor %}
{% endif %}
!
-router isis VyOS {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
+router isis VyOS {{ 'vrf ' + vrf if vrf is vyos_defined }}
net {{ net }}
-{% if dynamic_hostname is defined %}
+{% if dynamic_hostname is vyos_defined %}
hostname dynamic
{% endif %}
-{% if purge_originator is defined %}
+{% if purge_originator is vyos_defined %}
purge-originator
{% endif %}
-{% if set_attached_bit is defined %}
+{% if set_attached_bit is vyos_defined %}
set-attached-bit
{% endif %}
-{% if set_overload_bit is defined %}
+{% if set_overload_bit is vyos_defined %}
set-overload-bit
{% endif %}
-{% if domain_password is defined and domain_password is not none %}
-{% if domain_password.md5 is defined and domain_password.md5 is not none %}
+{% if domain_password.md5 is vyos_defined %}
domain-password md5 {{ domain_password.plaintext_password }}
-{% elif domain_password.plaintext_password is defined and domain_password.plaintext_password is not none %}
+{% elif domain_password.plaintext_password is vyos_defined %}
domain-password clear {{ domain_password.plaintext_password }}
-{% endif %}
{% endif %}
-{% if log_adjacency_changes is defined %}
+{% if log_adjacency_changes is vyos_defined %}
log-adjacency-changes
{% endif %}
-{% if lsp_gen_interval is defined and lsp_gen_interval is not none %}
+{% if lsp_gen_interval is vyos_defined %}
lsp-gen-interval {{ lsp_gen_interval }}
{% endif %}
-{% if lsp_mtu is defined and lsp_mtu is not none %}
+{% if lsp_mtu is vyos_defined %}
lsp-mtu {{ lsp_mtu }}
{% endif %}
-{% if lsp_refresh_interval is defined and lsp_refresh_interval is not none %}
+{% if lsp_refresh_interval is vyos_defined %}
lsp-refresh-interval {{ lsp_refresh_interval }}
{% endif %}
-{% if max_lsp_lifetime is defined and max_lsp_lifetime is not none %}
+{% if max_lsp_lifetime is vyos_defined %}
max-lsp-lifetime {{ max_lsp_lifetime }}
{% endif %}
-{% if spf_interval is defined and spf_interval is not none %}
+{% if spf_interval is vyos_defined %}
spf-interval {{ spf_interval }}
{% endif %}
-{% if traffic_engineering is defined and traffic_engineering is not none %}
-{% if traffic_engineering.enable is defined %}
+{% if traffic_engineering.enable is vyos_defined %}
mpls-te on
-{% endif %}
-{% if traffic_engineering.address is defined %}
+{% endif %}
+{% if traffic_engineering.address is vyos_defined %}
mpls-te router-address {{ traffic_engineering.address }}
+{% endif %}
+{% if traffic_engineering.inter_as is vyos_defined %}
+{% set level = '' %}
+{% if traffic_engineering.inter_as.level_1 is vyos_defined %}
+{% set level = ' level-1' %}
{% endif %}
-{% if traffic_engineering.inter_as is defined %}
-{% if traffic_engineering.inter_as.level_1 is defined %}
- mpls-te inter-as level-1
-{% endif %}
-{% if traffic_engineering.inter_as.level_1_2 is defined %}
- mpls-te inter-as level-1-2
-{% endif %}
-{% if traffic_engineering.inter_as.level_2 is defined %}
- mpls-te inter-as level-2-only
-{% endif %}
-{% else %}
- mpls-te inter-as
+{% if traffic_engineering.inter_as.level_1_2 is vyos_defined %}
+{% set level = ' level-1-2' %}
+{% endif %}
+{% if traffic_engineering.inter_as.level_2 is vyos_defined %}
+{% set level = ' level-2-only' %}
{% endif %}
+ mpls-te inter-as{{ level }}
{% endif %}
-{% if segment_routing is defined %}
-{% if segment_routing.enable is defined %}
+{% if segment_routing is vyos_defined %}
+{% if segment_routing.enable is vyos_defined %}
segment-routing on
{% endif %}
-{% if segment_routing.maximum_label_depth is defined %}
+{% if segment_routing.maximum_label_depth is vyos_defined %}
segment-routing node-msd {{ segment_routing.maximum_label_depth }}
{% endif %}
-{% if segment_routing.global_block is defined %}
+{% if segment_routing.global_block is vyos_defined %}
+{% if segment_routing.local_block is vyos_defined %}
+ segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }} local-block {{ segment_routing.local_block.low_label_value }} {{ segment_routing.local_block.high_label_value }}
+{% else %}
segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }}
+{% endif %}
{% endif %}
-{% if segment_routing.local_block is defined %}
- segment-routing local-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.local_block.high_label_value }}
-{% endif %}
-{% if segment_routing.prefix is defined %}
+{% if segment_routing.prefix is vyos_defined %}
{% for prefixes in segment_routing.prefix %}
-{% if segment_routing.prefix[prefixes].absolute is defined %}
-{% if segment_routing.prefix[prefixes].absolute.value is defined %}
+{% if segment_routing.prefix[prefixes].absolute is vyos_defined %}
+{% if segment_routing.prefix[prefixes].absolute.value is vyos_defined %}
segment-routing prefix {{ prefixes }} absolute {{ segment_routing.prefix[prefixes].absolute.value }}
-{% if segment_routing.prefix[prefixes].absolute.explicit_null is defined %}
+{% if segment_routing.prefix[prefixes].absolute.explicit_null is vyos_defined %}
segment-routing prefix {{ prefixes }} absolute {{ segment_routing.prefix[prefixes].absolute.value }} explicit-null
{% endif %}
-{% if segment_routing.prefix[prefixes].absolute.no_php_flag is defined %}
+{% if segment_routing.prefix[prefixes].absolute.no_php_flag is vyos_defined %}
segment-routing prefix {{ prefixes }} absolute {{ segment_routing.prefix[prefixes].absolute.value }} no-php-flag
{% endif %}
{% endif %}
-{% if segment_routing.prefix[prefixes].index is defined %}
-{% if segment_routing.prefix[prefixes].index.value is defined %}
+{% if segment_routing.prefix[prefixes].index is vyos_defined %}
+{% if segment_routing.prefix[prefixes].index.value is vyos_defined %}
segment-routing prefix {{ prefixes }} index {{ segment_routing.prefix[prefixes].index.value }}
-{% if segment_routing.prefix[prefixes].index.explicit_null is defined %}
+{% if segment_routing.prefix[prefixes].index.explicit_null is vyos_defined %}
segment-routing prefix {{ prefixes }} index {{ segment_routing.prefix[prefixes].index.value }} explicit-null
{% endif %}
-{% if segment_routing.prefix[prefixes].index.no_php_flag is defined %}
+{% if segment_routing.prefix[prefixes].index.no_php_flag is vyos_defined %}
segment-routing prefix {{ prefixes }} index {{ segment_routing.prefix[prefixes].index.value }} no-php-flag
{% endif %}
{% endif %}
@@ -148,57 +147,51 @@ router isis VyOS {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
{% endfor %}
{% endif %}
{% endif %}
-{% if spf_delay_ietf is defined and spf_delay_ietf.init_delay is defined and spf_delay_ietf.init_delay is not none %}
+{% if spf_delay_ietf.init_delay is vyos_defined %}
spf-delay-ietf init-delay {{ spf_delay_ietf.init_delay }} short-delay {{ spf_delay_ietf.short_delay }} long-delay {{ spf_delay_ietf.long_delay }} holddown {{ spf_delay_ietf.holddown }} time-to-learn {{ spf_delay_ietf.time_to_learn }}
{% endif %}
-{% if area_password is defined and area_password is not none %}
-{% if area_password.md5 is defined and area_password.md5 is not none %}
+{% if area_password.md5 is vyos_defined %}
area-password md5 {{ area_password.md5 }}
-{% elif area_password.plaintext_password is defined and area_password.plaintext_password is not none %}
+{% elif area_password.plaintext_password is vyos_defined %}
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 %}
+{% if default_information.originate is vyos_defined %}
{% 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 }}
+ default-information originate {{ afi }} {{ level | replace('_', '-') }} {{ 'always' if level_config.always is vyos_defined }} {{ 'route-map ' ~ level_config.route_map if level_config.route_map is vyos_defined }} {{ 'metric ' ~ level_config.metric if level_config.metric is vyos_defined }}
{% endfor %}
{% endfor %}
{% endif %}
-{% 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 %}
+{% if redistribute.ipv4 is vyos_defined %}
+{% for protocol, protocol_options in redistribute.ipv4.items() %}
+{% for level, level_config in protocol_options.items() %}
+{% if level_config.metric is vyos_defined %}
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 vyos_defined %}
redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }} route-map {{ level_config.route_map }}
-{% else %}
+{% else %}
redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }}
-{% endif %}
-{% endfor %}
+{% endif %}
{% 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 %}
+{% endfor %}
+{% endif %}
+{% if redistribute.ipv6 is vyos_defined %}
+{% for protocol, protocol_options in redistribute.ipv6.items() %}
+{% for level, level_config in protocol_options.items() %}
+{% if level_config.metric is vyos_defined %}
redistribute ipv6 {{ 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 vyos_defined %}
redistribute ipv6 {{ protocol }} {{ level | replace('_', '-') }} route-map {{ level_config.route_map }}
-{% else %}
+{% else %}
redistribute ipv6 {{ protocol }} {{ level | replace('_', '-') }}
-{% endif %}
-{% endfor %}
+{% endif %}
{% endfor %}
-{% endif %}
+{% endfor %}
{% endif %}
-{% if level is defined and level is not none %}
-{% if level == 'level-2' %}
+{% if level is vyos_defined('level-2') %}
is-type level-2-only
-{% else %}
+{% elif level is vyos_defined %}
is-type {{ level }}
-{% endif %}
{% endif %}
exit
! \ No newline at end of file
diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl
index 537ea4025..5a67b5cdf 100644
--- a/data/templates/frr/ldpd.frr.tmpl
+++ b/data/templates/frr/ldpd.frr.tmpl
@@ -1,190 +1,157 @@
!
-{% if ldp is defined %}
+{% if ldp is vyos_defined %}
mpls ldp
-{% if ldp.router_id is defined %}
+{% if ldp.router_id is vyos_defined %}
router-id {{ ldp.router_id }}
-{% endif %}
-{% if ldp.parameters is defined %}
-{% if ldp.parameters.cisco_interop_tlv is defined %}
+{% endif %}
+{% if ldp.parameters.cisco_interop_tlv is vyos_defined %}
dual-stack cisco-interop
-{% endif %}
-{% if ldp.parameters.transport_prefer_ipv4 is defined%}
+{% endif %}
+{% if ldp.parameters.transport_prefer_ipv4 is vyos_defined %}
dual-stack transport-connection prefer ipv4
-{% endif %}
-{% if ldp.parameters.ordered_control is defined%}
+{% endif %}
+{% if ldp.parameters.ordered_control is vyos_defined %}
ordered-control
-{% endif %}
-{% endif %}
-{% if ldp.neighbor is defined %}
-{% for neighbors in ldp.neighbor %}
-{% if ldp.neighbor[neighbors].password is defined %}
- neighbor {{ neighbors }} password {{ ldp.neighbor[neighbors].password }}
-{% endif %}
-{% if ldp.neighbor[neighbors].ttl_security is defined %}
-{% if 'disable' in ldp.neighbor[neighbors].ttl_security %}
+{% endif %}
+{% if ldp.neighbor is vyos_defined %}
+{% for neighbor, neighbor_config in ldp.neighbor %}
+{% if neighbor_config.password is vyos_defined %}
+ neighbor {{ neighbors }} password {{ neighbor_config.password }}
+{% endif %}
+{% if neighbor_config.ttl_security is vyos_defined %}
+{% if neighbor_config.ttl_security.disable is vyos_defined%}
neighbor {{ neighbors }} ttl-security disable
-{% else %}
- neighbor {{ neighbors }} ttl-security hops {{ ldp.neighbor[neighbors].ttl_security }}
-{% endif %}
-{% endif %}
-{% if ldp.neighbor[neighbors].session_holdtime is defined %}
- neighbor {{ neighbors }} session holdtime {{ ldp.neighbor[neighbors].session_holdtime }}
-{% endif %}
-{% endfor %}
-{% endif %}
+{% else %}
+ neighbor {{ neighbors }} ttl-security hops {{ neighbor_config.ttl_security }}
+{% endif %}
+{% endif %}
+{% if neighbor_config.session_holdtime is vyos_defined %}
+ neighbor {{ neighbors }} session holdtime {{ neighbor_config.session_holdtime }}
+{% endif %}
+{% endfor %}
+{% endif %}
!
-{% if ldp.discovery is defined %}
-{% if ldp.discovery.transport_ipv4_address is defined %}
+{% if ldp.discovery.transport_ipv4_address is vyos_defined %}
address-family ipv4
-{% if ldp.allocation is defined %}
-{% if ldp.allocation.ipv4 is defined %}
-{% if ldp.allocation.ipv4.access_list is defined %}
+{% if ldp.allocation.ipv4.access_list is vyos_defined %}
label local allocate for {{ ldp.allocation.ipv4.access_list }}
-{% endif %}
-{% endif %}
-{% else %}
+{% else %}
label local allocate host-routes
-{% endif %}
-{% if ldp.discovery.transport_ipv4_address is defined %}
+{% endif %}
+{% if ldp.discovery.transport_ipv4_address is vyos_defined %}
discovery transport-address {{ ldp.discovery.transport_ipv4_address }}
-{% endif %}
-{% if ldp.discovery.hello_ipv4_holdtime is defined %}
+{% endif %}
+{% if ldp.discovery.hello_ipv4_holdtime is vyos_defined %}
discovery hello holdtime {{ ldp.discovery.hello_ipv4_holdtime }}
-{% endif %}
-{% if ldp.discovery.hello_ipv4_interval is defined %}
+{% endif %}
+{% if ldp.discovery.hello_ipv4_interval is vyos_defined %}
discovery hello interval {{ ldp.discovery.hello_ipv4_interval }}
-{% endif %}
-{% if ldp.discovery.session_ipv4_holdtime is defined %}
+{% endif %}
+{% if ldp.discovery.session_ipv4_holdtime is vyos_defined %}
session holdtime {{ ldp.discovery.session_ipv4_holdtime }}
-{% endif %}
-{% if ldp.import is defined %}
-{% if ldp.import.ipv4 is defined %}
-{% if ldp.import.ipv4.import_filter is defined %}
-{% if ldp.import.ipv4.import_filter.filter_access_list is defined %}
-{% if ldp.import.ipv4.import_filter.neighbor_access_list is defined %}
+{% endif %}
+{% if ldp.import.ipv4.import_filter.filter_access_list is vyos_defined %}
+{% if ldp.import.ipv4.import_filter.neighbor_access_list is vyos_defined %}
label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }} from {{ ldp.import.ipv4.import_filter.neighbor_access_list }}
-{% else %}
+{% else %}
label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% if ldp.export is defined %}
-{% if ldp.export.ipv4 is defined %}
-{% if ldp.export.ipv4.explicit_null is defined %}
+{% endif %}
+{% endif %}
+{% if ldp.export.ipv4.explicit_null is vyos_defined %}
label local advertise explicit-null
-{% endif %}
-{% if ldp.export.ipv4.export_filter is defined %}
-{% if ldp.export.ipv4.export_filter.filter_access_list is defined %}
-{% if ldp.export.ipv4.export_filter.neighbor_access_list is defined %}
+{% endif %}
+{% if ldp.export.ipv4.export_filter.filter_access_list is vyos_defined %}
+{% if ldp.export.ipv4.export_filter.neighbor_access_list is vyos_defined %}
label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }} to {{ ldp.export.ipv4.export_filter.neighbor_access_list }}
-{% else %}
+{% else %}
label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% if ldp.targeted_neighbor is defined %}
-{% if ldp.targeted_neighbor.ipv4.enable is defined %}
+{% endif %}
+{% endif %}
+{% if ldp.targeted_neighbor is vyos_defined %}
+{% if ldp.targeted_neighbor.ipv4.enable is vyos_defined %}
discovery targeted-hello accept
-{% endif %}
-{% if ldp.targeted_neighbor.ipv4.hello_holdtime is defined %}
+{% endif %}
+{% if ldp.targeted_neighbor.ipv4.hello_holdtime is vyos_defined %}
discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv4.hello_holdtime }}
-{% endif %}
-{% if ldp.targeted_neighbor.ipv4.hello_interval is defined %}
+{% endif %}
+{% if ldp.targeted_neighbor.ipv4.hello_interval is vyos_defined %}
discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv4.hello_interval }}
-{% endif %}
-{% for addresses in ldp.targeted_neighbor.ipv4.address %}
- neighbor {{addresses}} targeted
-{% endfor %}
-{% endif %}
-{% for interfaces in ldp.interface %}
- interface {{interfaces}}
-{% endfor %}
+{% endif %}
+{% for addresses in ldp.targeted_neighbor.ipv4.address %}
+ neighbor {{ addresses }} targeted
+{% endfor %}
+{% endif %}
+{% if ldp.interface is vyos_defined %}
+{% for interface in ldp.interface %}
+ interface {{ interface }}
+ exit
+{% endfor %}
+{% endif %}
exit-address-family
-{% else %}
+{% else %}
no address-family ipv4
-{% endif %}
-{% endif %}
+{% endif %}
!
-{% if ldp.discovery is defined %}
-{% if ldp.discovery.transport_ipv6_address is defined %}
+{% if ldp.discovery.transport_ipv6_address is vyos_defined %}
address-family ipv6
-{% if ldp.allocation is defined %}
-{% if ldp.allocation.ipv6 is defined %}
-{% if ldp.allocation.ipv6.access_list6 is defined %}
+{% if ldp.allocation.ipv6.access_list6 is vyos_defined %}
label local allocate for {{ ldp.allocation.ipv6.access_list6 }}
-{% endif %}
-{% endif %}
-{% else %}
+{% else %}
label local allocate host-routes
-{% endif %}
-{% if ldp.discovery.transport_ipv6_address is defined %}
+{% endif %}
+{% if ldp.discovery.transport_ipv6_address is vyos_defined %}
discovery transport-address {{ ldp.discovery.transport_ipv6_address }}
-{% endif %}
-{% if ldp.discovery.hello_ipv6_holdtime is defined %}
+{% endif %}
+{% if ldp.discovery.hello_ipv6_holdtime is vyos_defined %}
discovery hello holdtime {{ ldp.discovery.hello_ipv6_holdtime }}
-{% endif %}
-{% if ldp.discovery.hello_ipv6_interval is defined %}
+{% endif %}
+{% if ldp.discovery.hello_ipv6_interval is vyos_defined %}
discovery hello interval {{ ldp.discovery.hello_ipv6_interval }}
-{% endif %}
-{% if ldp.discovery.session_ipv6_holdtime is defined %}
+{% endif %}
+{% if ldp.discovery.session_ipv6_holdtime is vyos_defined %}
session holdtime {{ ldp.discovery.session_ipv6_holdtime }}
-{% endif %}
-{% if ldp.import is defined %}
-{% if ldp.import.ipv6 is defined %}
-{% if ldp.import.ipv6.import_filter is defined %}
-{% if ldp.import.ipv6.import_filter.filter_access_list6 is defined %}
-{% if ldp.import.ipv6.import_filter.neighbor_access_list6 is defined %}
+{% endif %}
+{% if ldp.import.ipv6.import_filter.filter_access_list6 is vyos_defined %}
+{% if ldp.import.ipv6.import_filter.neighbor_access_list6 is vyos_defined %}
label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }} from {{ ldp.import.ipv6.import_filter.neighbor_access_list6 }}
-{% else %}
+{% else %}
label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% if ldp.export is defined %}
-{% if ldp.export.ipv6 is defined %}
-{% if ldp.export.ipv6.explicit_null is defined %}
+{% endif %}
+{% endif %}
+{% if ldp.export.ipv6.explicit_null is vyos_defined %}
label local advertise explicit-null
-{% endif %}
-{% if ldp.export.ipv6.export_filter is defined %}
-{% if ldp.export.ipv6.export_filter.filter_access_list6 is defined %}
-{% if ldp.export.ipv6.export_filter.neighbor_access_list6 is defined %}
+{% endif %}
+{% if ldp.export.ipv6.export_filter.filter_access_list6 is vyos_defined %}
+{% if ldp.export.ipv6.export_filter.neighbor_access_list6 is vyos_defined %}
label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }} to {{ ldp.export.ipv6.export_filter.neighbor_access_list6 }}
-{% else %}
+{% else %}
label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% if ldp.targeted_neighbor is defined %}
-{% if ldp.targeted_neighbor.ipv6.enable is defined %}
+{% endif %}
+{% endif %}
+{% if ldp.targeted_neighbor is vyos_defined %}
+{% if ldp.targeted_neighbor.ipv6.enable is vyos_defined %}
discovery targeted-hello accept
-{% endif %}
-{% if ldp.targeted_neighbor.ipv6.hello_holdtime is defined %}
+{% endif %}
+{% if ldp.targeted_neighbor.ipv6.hello_holdtime is vyos_defined %}
discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv6.hello_holdtime }}
-{% endif %}
-{% if ldp.targeted_neighbor.ipv6.hello_interval is defined %}
+{% endif %}
+{% if ldp.targeted_neighbor.ipv6.hello_interval is vyos_defined %}
discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv6.hello_interval }}
-{% endif %}
-{% for addresses in ldp.targeted_neighbor.ipv6.address %}
- neighbor {{addresses}} targeted
-{% endfor %}
-{% endif %}
-{% for interfaces in ldp.interface %}
- interface {{interfaces}}
-{% endfor %}
+{% endif %}
+{% for addresses in ldp.targeted_neighbor.ipv6.address %}
+ neighbor {{ addresses }} targeted
+{% endfor %}
+{% endif %}
+{% if ldp.interface is vyos_defined %}
+{% for interface in ldp.interface %}
+ interface {{ interface }}
+{% endfor %}
+{% endif %}
exit-address-family
-{% else %}
+{% else %}
no address-family ipv6
-{% endif %}
+{% endif %}
!
-{% endif %}
exit
{% endif %}
!
diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl
index 8279e5abb..325f05213 100644
--- a/data/templates/frr/ospf6d.frr.tmpl
+++ b/data/templates/frr/ospf6d.frr.tmpl
@@ -1,47 +1,47 @@
!
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
-interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
-{% if iface_config.area is defined and iface_config.area is not none %}
+interface {{ iface }}
+{% if iface_config.area is vyos_defined %}
ipv6 ospf6 area {{ iface_config.area }}
{% endif %}
-{% if iface_config.cost is defined and iface_config.cost is not none %}
+{% if iface_config.cost is vyos_defined %}
ipv6 ospf6 cost {{ iface_config.cost }}
{% endif %}
-{% if iface_config.priority is defined and iface_config.priority is not none %}
+{% if iface_config.priority is vyos_defined %}
ipv6 ospf6 priority {{ iface_config.priority }}
{% endif %}
-{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %}
+{% if iface_config.hello_interval is vyos_defined %}
ipv6 ospf6 hello-interval {{ iface_config.hello_interval }}
{% endif %}
-{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %}
+{% if iface_config.retransmit_interval is vyos_defined %}
ipv6 ospf6 retransmit-interval {{ iface_config.retransmit_interval }}
{% endif %}
-{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %}
+{% if iface_config.transmit_delay is vyos_defined %}
ipv6 ospf6 transmit-delay {{ iface_config.transmit_delay }}
{% endif %}
-{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %}
+{% if iface_config.dead_interval is vyos_defined %}
ipv6 ospf6 dead-interval {{ iface_config.dead_interval }}
{% endif %}
-{% if iface_config.bfd is defined %}
+{% if iface_config.bfd is vyos_defined %}
ipv6 ospf6 bfd
-{% if iface_config.bfd.profile is defined and iface_config.bfd.profile is not none %}
+{% endif %}
+{% if iface_config.bfd.profile is vyos_defined %}
ipv6 ospf6 bfd profile {{ iface_config.bfd.profile }}
-{% endif %}
{% endif %}
-{% if iface_config.mtu_ignore is defined %}
+{% if iface_config.mtu_ignore is vyos_defined %}
ipv6 ospf6 mtu-ignore
{% endif %}
-{% if iface_config.ifmtu is defined and iface_config.ifmtu is not none %}
+{% if iface_config.ifmtu is vyos_defined %}
ipv6 ospf6 ifmtu {{ iface_config.ifmtu }}
{% endif %}
-{% if iface_config.network is defined and iface_config.network is not none %}
+{% if iface_config.network is vyos_defined %}
ipv6 ospf6 network {{ iface_config.network }}
{% endif %}
-{% if iface_config.instance_id is defined and iface_config.instance_id is not none %}
+{% if iface_config.instance_id is vyos_defined %}
ipv6 ospf6 instance-id {{ iface_config.instance_id }}
{% endif %}
-{% if iface_config.passive is defined %}
+{% if iface_config.passive is vyos_defined %}
ipv6 ospf6 passive
{% endif %}
exit
@@ -49,50 +49,46 @@ exit
{% endfor %}
{% endif %}
!
-router ospf6 {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
-{% if area is defined and area is not none %}
+router ospf6 {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
+{% if area is vyos_defined %}
{% for area_id, area_config in area.items() %}
-{% if area_config.area_type is defined and area_config.area_type is not none %}
+{% if area_config.area_type is vyos_defined %}
{% for type, type_config in area_config.area_type.items() %}
- area {{ area_id }} {{ type }} {{ 'default-information-originate' if type_config.default_information_originate is defined }} {{ 'no-summary' if type_config.no_summary is defined }}
+ area {{ area_id }} {{ type }} {{ 'default-information-originate' if type_config.default_information_originate is vyos_defined }} {{ 'no-summary' if type_config.no_summary is vyos_defined }}
{% endfor %}
{% endif %}
-{% if area_config.range is defined and area_config.range is not none %}
+{% if area_config.range is vyos_defined %}
{% for prefix, prefix_config in area_config.range.items() %}
- area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }}
+ area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is vyos_defined }} {{ 'not-advertise' if prefix_config.not_advertise is vyos_defined }}
{% endfor %}
{% endif %}
-{% if area_config.export_list is defined and area_config.export_list is not none %}
+{% if area_config.export_list is vyos_defined %}
area {{ area_id }} export-list {{ area_config.export_list }}
{% endif %}
-{% if area_config.import_list is defined and area_config.import_list is not none %}
+{% if area_config.import_list is vyos_defined %}
area {{ area_id }} import-list {{ area_config.import_list }}
{% endif %}
{% endfor %}
{% endif %}
auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }}
-{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %}
- default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }}
+{% if default_information.originate is vyos_defined %}
+ default-information originate {{ 'always' if default_information.originate.always is vyos_defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is vyos_defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is vyos_defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is vyos_defined }}
{% endif %}
-{% if distance is defined and distance is not none %}
-{% if distance.global is defined and distance.global is not none %}
+{% if distance.global is vyos_defined %}
distance {{ distance.global }}
-{% endif %}
-{% if distance.ospfv3 is defined and distance.ospfv3 is not none %}
- distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }}
-{% endif %}
{% endif %}
-{% if log_adjacency_changes is defined %}
- log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }}
+{% if distance.ospfv3 is vyos_defined %}
+ distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is vyos_defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is vyos_defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is vyos_defined }}
+{% endif %}
+{% if log_adjacency_changes is vyos_defined %}
+ log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is vyos_defined }}
{% endif %}
-{% if parameters is defined and parameters is not none %}
-{% if parameters.router_id is defined and parameters.router_id is not none %}
+{% if parameters.router_id is vyos_defined %}
ospf6 router-id {{ parameters.router_id }}
-{% endif %}
{% endif %}
-{% if redistribute is defined and redistribute is not none %}
+{% if redistribute is vyos_defined %}
{% for protocol, options in redistribute.items() %}
- redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }}
+ redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is vyos_defined }}
{% endfor %}
{% endif %}
exit
diff --git a/data/templates/frr/ospfd.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl
index af66baf53..7c6738181 100644
--- a/data/templates/frr/ospfd.frr.tmpl
+++ b/data/templates/frr/ospfd.frr.tmpl
@@ -1,115 +1,117 @@
!
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
-interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
-{% if iface_config.authentication is defined and iface_config.authentication is not none %}
-{% if iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %}
+interface {{ iface }}
+{% if iface_config.authentication.plaintext_password is vyos_defined %}
ip ospf authentication-key {{ iface_config.authentication.plaintext_password }}
-{% elif iface_config.authentication.md5 is defined %}
+{% elif iface_config.authentication.md5 is vyos_defined %}
ip ospf authentication message-digest
-{% if iface_config.authentication.md5.key_id is defined and iface_config.authentication.md5.key_id is not none %}
-{% for key, key_config in iface_config.authentication.md5.key_id.items() %}
+{% if iface_config.authentication.md5.key_id is vyos_defined %}
+{% for key, key_config in iface_config.authentication.md5.key_id.items() %}
ip ospf message-digest-key {{ key }} md5 {{ key_config.md5_key }}
-{% endfor %}
-{% endif %}
+{% endfor %}
{% endif %}
{% endif %}
-{% if iface_config.area is defined and iface_config.area is not none %}
+{% if iface_config.area is vyos_defined %}
ip ospf area {{ iface_config.area }}
{% endif %}
-{% if iface_config.bandwidth is defined and iface_config.bandwidth is not none %}
+{% if iface_config.bandwidth is vyos_defined %}
bandwidth {{ iface_config.bandwidth }}
{% endif %}
-{% if iface_config.cost is defined and iface_config.cost is not none %}
+{% if iface_config.cost is vyos_defined %}
ip ospf cost {{ iface_config.cost }}
{% endif %}
-{% if iface_config.priority is defined and iface_config.priority is not none %}
+{% if iface_config.priority is vyos_defined %}
ip ospf priority {{ iface_config.priority }}
{% endif %}
-{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %}
+{% if iface_config.hello_interval is vyos_defined %}
ip ospf hello-interval {{ iface_config.hello_interval }}
{% endif %}
-{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %}
+{% if iface_config.retransmit_interval is vyos_defined %}
ip ospf retransmit-interval {{ iface_config.retransmit_interval }}
{% endif %}
-{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %}
+{% if iface_config.transmit_delay is vyos_defined %}
ip ospf transmit-delay {{ iface_config.transmit_delay }}
{% endif %}
-{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %}
+{% if iface_config.dead_interval is vyos_defined %}
ip ospf dead-interval {{ iface_config.dead_interval }}
-{% elif iface_config.hello_multiplier is defined and iface_config.hello_multiplier is not none %}
+{% elif iface_config.hello_multiplier is vyos_defined %}
ip ospf dead-interval minimal hello-multiplier {{ iface_config.hello_multiplier }}
{% endif %}
-{% if iface_config.bfd is defined %}
+{% if iface_config.bfd is vyos_defined %}
ip ospf bfd
-{% if iface_config.bfd.profile is defined and iface_config.bfd.profile is not none %}
+{% endif %}
+{% if iface_config.bfd.profile is vyos_defined %}
ip ospf bfd profile {{ iface_config.bfd.profile }}
-{% endif %}
{% endif %}
-{% if iface_config.mtu_ignore is defined %}
+{% if iface_config.mtu_ignore is vyos_defined %}
ip ospf mtu-ignore
{% endif %}
-{% if iface_config.network is defined and iface_config.network is not none %}
+{% if iface_config.network is vyos_defined %}
ip ospf network {{ iface_config.network }}
{% endif %}
-{% if iface_config.passive is defined %}
- {{ 'no ' if iface_config.passive.disable is defined }}ip ospf passive
+{% if iface_config.passive is vyos_defined %}
+ {{ 'no ' if iface_config.passive.disable is vyos_defined }}ip ospf passive
{% endif %}
exit
!
{% endfor %}
{% endif %}
!
-router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
-{% if access_list is defined and access_list is not none %}
+router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
+{% if access_list is vyos_defined %}
{% for acl, acl_config in access_list.items() %}
-{% for protocol in acl_config.export if acl_config.export is defined %}
+{% for protocol in acl_config.export if acl_config.export is vyos_defined %}
distribute-list {{ acl }} out {{ protocol }}
{% endfor %}
{% endfor %}
{% endif %}
-{% if area is defined and area is not none %}
+{% if area is vyos_defined %}
{% for area_id, area_config in area.items() %}
-{% if area_config.area_type is defined and area_config.area_type is not none %}
+{% if area_config.area_type is vyos_defined %}
{% for type, type_config in area_config.area_type.items() if type != 'normal' %}
- area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }}
-{% if type_config.default_cost is defined and type_config.default_cost is not none %}
+ area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is vyos_defined }}
+{% if type_config.default_cost is vyos_defined %}
area {{ area_id }} default-cost {{ type_config.default_cost }}
{% endif %}
{% endfor %}
{% endif %}
-{% if area_config.authentication is defined and area_config.authentication is not none %}
- area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication == 'md5' }}
+{% if area_config.authentication is vyos_defined %}
+ area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication is vyos_defined('md5') }}
{% endif %}
-{% for network in area_config.network if area_config.network is defined %}
+{% for network in area_config.network if area_config.network is vyos_defined %}
network {{ network }} area {{ area_id }}
{% endfor %}
-{% if area_config.range is defined and area_config.range is not none %}
+{% if area_config.range is vyos_defined %}
{% for range, range_config in area_config.range.items() %}
-{% if range_config.cost is defined and range_config.cost is not none %}
+{% if range_config.cost is vyos_defined %}
area {{ area_id }} range {{ range }} cost {{ range_config.cost }}
{% endif %}
-{% if range_config.not_advertise is defined %}
+{% if range_config.not_advertise is vyos_defined %}
area {{ area_id }} range {{ range }} not-advertise
{% endif %}
-{% if range_config.substitute is defined and range_config.substitute is not none %}
+{% if range_config.substitute is vyos_defined %}
area {{ area_id }} range {{ range }} substitute {{ range_config.substitute }}
{% endif %}
{% endfor %}
{% endif %}
-{% if area_config.shortcut is defined and area_config.shortcut is not none %}
+{% if area_config.export_list is vyos_defined %}
+ area {{ area_id }} export-list {{ area_config.export_list }}
+{% endif %}
+{% if area_config.import_list is vyos_defined %}
+ area {{ area_id }} import-list {{ area_config.import_list }}
+{% endif %}
+{% if area_config.shortcut is vyos_defined %}
area {{ area_id }} shortcut {{ area_config.shortcut }}
{% endif %}
-{% if area_config.virtual_link is defined and area_config.virtual_link is not none %}
+{% if area_config.virtual_link is vyos_defined %}
{% for link, link_config in area_config.virtual_link.items() %}
-{% if link_config.authentication is defined and link_config.authentication is not none %}
-{% if link_config.authentication.plaintext_password is defined and link_config.authentication.plaintext_password is not none %}
+{% if link_config.authentication.plaintext_password is vyos_defined %}
area {{ area_id }} virtual-link {{ link }} authentication-key {{ link_config.authentication.plaintext_password }}
-{% elif link_config.authentication.md5 is defined and link_config.authentication.md5.key_id is defined and link_config.authentication.md5.key_id is not none %}
-{% for key, key_config in link_config.authentication.md5.key_id.items() %}
+{% elif link_config.authentication.md5.key_id is vyos_defined %}
+{% for key, key_config in link_config.authentication.md5.key_id.items() %}
area {{ area_id }} virtual-link {{ link }} message-digest-key {{ key }} md5 {{ key_config.md5_key }}
-{% endfor %}
-{% endif %}
+{% endfor %}
{% endif %}
{# The following values are default values #}
area {{ area_id }} virtual-link {{ link }} hello-interval {{ link_config.hello_interval }} retransmit-interval {{ link_config.retransmit_interval }} transmit-delay {{ link_config.transmit_delay }} dead-interval {{ link_config.dead_interval }}
@@ -117,72 +119,69 @@ router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
{% endif %}
{% endfor %}
{% endif %}
-{% if auto_cost is defined and auto_cost.reference_bandwidth is defined and auto_cost.reference_bandwidth is not none %}
+{% if auto_cost.reference_bandwidth is vyos_defined %}
auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }}
{% endif %}
-{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %}
- default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }}
+{% if default_information.originate is vyos_defined %}
+ default-information originate {{ 'always' if default_information.originate.always is vyos_defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is vyos_defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is vyos_defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is vyos_defined }}
{% endif %}
-{% if default_metric is defined and default_metric is not none %}
+{% if default_metric is vyos_defined %}
default-metric {{ default_metric }}
{% endif %}
-{% if distance is defined and distance is not none %}
-{% if distance.global is defined and distance.global is not none %}
+{% if maximum_paths is vyos_defined %}
+ maximum-paths {{ maximum_paths }}
+{% endif %}
+{% if distance.global is vyos_defined %}
distance {{ distance.global }}
-{% endif %}
-{% if distance.ospf is defined and distance.ospf is not none %}
- distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is defined }}
-{% endif %}
{% endif %}
-{% if log_adjacency_changes is defined %}
- log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }}
+{% if distance.ospf is vyos_defined %}
+ distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is vyos_defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is vyos_defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is vyos_defined }}
+{% endif %}
+{% if log_adjacency_changes is vyos_defined %}
+ log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is vyos_defined }}
{% endif %}
-{% if max_metric is defined and max_metric.router_lsa is defined and max_metric.router_lsa is not none %}
-{% if max_metric.router_lsa.administrative is defined %}
+{% if max_metric.router_lsa.administrative is vyos_defined %}
max-metric router-lsa administrative
-{% endif %}
-{% if max_metric.router_lsa.on_shutdown is defined and max_metric.router_lsa.on_shutdown is not none %}
+{% endif %}
+{% if max_metric.router_lsa.on_shutdown is vyos_defined %}
max-metric router-lsa on-shutdown {{ max_metric.router_lsa.on_shutdown }}
-{% endif %}
-{% if max_metric.router_lsa.on_startup is defined and max_metric.router_lsa.on_startup is not none %}
+{% endif %}
+{% if max_metric.router_lsa.on_startup is vyos_defined %}
max-metric router-lsa on-startup {{ max_metric.router_lsa.on_startup }}
-{% endif %}
{% endif %}
-{% if mpls_te is defined and mpls_te.enable is defined %}
+{% if mpls_te.enable is vyos_defined %}
mpls-te on
mpls-te router-address {{ mpls_te.router_address }}
{% endif %}
-{% if neighbor is defined and neighbor is not none%}
+{% if neighbor is vyos_defined %}
{% for address, address_config in neighbor.items() %}
- neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is defined }}
+ neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is vyos_defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is vyos_defined }}
{% endfor %}
{% endif %}
-{% if parameters is defined and parameters is not none %}
-{% if parameters.abr_type is defined and parameters.abr_type is not none %}
+{% if parameters.abr_type is vyos_defined %}
ospf abr-type {{ parameters.abr_type }}
-{% endif %}
-{% if parameters.router_id is defined and parameters.router_id is not none %}
+{% endif %}
+{% if parameters.router_id is vyos_defined %}
ospf router-id {{ parameters.router_id }}
-{% endif %}
{% endif %}
-{% if passive_interface is defined and passive_interface.default is defined %}
+{% if passive_interface.default is vyos_defined %}
passive-interface default
{% endif %}
-{% if redistribute is defined and redistribute is not none %}
+{% if redistribute is vyos_defined %}
{% for protocol, protocols_options in redistribute.items() %}
{% if protocol == 'table' %}
{% for table, table_options in protocols_options.items() %}
- redistribute {{ protocol }} {{ table }} {{ 'metric ' + table_options.metric if table_options.metric is defined }} {{ 'metric-type ' + table_options.metric_type if table_options.metric_type is defined }} {{ 'route-map ' + table_options.route_map if table_options.route_map is defined }}
+ redistribute {{ protocol }} {{ table }} {{ 'metric ' + table_options.metric if table_options.metric is vyos_defined }} {{ 'metric-type ' + table_options.metric_type if table_options.metric_type is vyos_defined }} {{ 'route-map ' + table_options.route_map if table_options.route_map is vyos_defined }}
{% endfor %}
{% else %}
- redistribute {{ protocol }} {{ 'metric ' + protocols_options.metric if protocols_options.metric is defined }} {{ 'metric-type ' + protocols_options.metric_type if protocols_options.metric_type is defined }} {{ 'route-map ' + protocols_options.route_map if protocols_options.route_map is defined }}
+ redistribute {{ protocol }} {{ 'metric ' + protocols_options.metric if protocols_options.metric is vyos_defined }} {{ 'metric-type ' + protocols_options.metric_type if protocols_options.metric_type is vyos_defined }} {{ 'route-map ' + protocols_options.route_map if protocols_options.route_map is vyos_defined }}
{% endif %}
{% endfor %}
{% endif %}
-{% if refresh is defined and refresh.timers is defined and refresh.timers is not none %}
+{% if refresh.timers is vyos_defined %}
refresh timer {{ refresh.timers }}
{% endif %}
-{% if timers is defined and timers.throttle is defined and timers.throttle.spf is defined and timers.throttle.spf is not none %}
+{% if timers.throttle.spf.delay is vyos_defined and timers.throttle.spf.initial_holdtime is vyos_defined and timers.throttle.spf.max_holdtime is vyos_defined %}
{# Timer values have default values #}
timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }}
{% endif %}
diff --git a/data/templates/frr/policy.frr.tmpl b/data/templates/frr/policy.frr.tmpl
index d3d3957a5..814dbf761 100644
--- a/data/templates/frr/policy.frr.tmpl
+++ b/data/templates/frr/policy.frr.tmpl
@@ -1,18 +1,18 @@
-{% if access_list is defined and access_list is not none %}
+{% if access_list is vyos_defined %}
{% for acl, acl_config in access_list.items() | natural_sort %}
-{% if acl_config.description is defined and acl_config.description is not none %}
+{% if acl_config.description is vyos_defined %}
access-list {{ acl }} remark {{ acl_config.description }}
{% endif %}
-{% if acl_config.rule is defined and acl_config.rule is not none %}
+{% if acl_config.rule is vyos_defined %}
{% for rule, rule_config in acl_config.rule.items() | natural_sort %}
{% set ip = '' %}
{% set src = '' %}
{% set src_mask = '' %}
-{% if rule_config.source is defined and rule_config.source.any is defined %}
+{% if rule_config.source.any is vyos_defined %}
{% set src = 'any' %}
-{% elif rule_config.source is defined and rule_config.source.host is defined and rule_config.source.host is not none %}
-{% set src = 'host ' + rule_config.source.host %}
-{% elif rule_config.source is defined and rule_config.source.network is defined and rule_config.source.network is not none %}
+{% elif rule_config.source.host is vyos_defined %}
+{% set src = 'host ' ~ rule_config.source.host %}
+{% elif rule_config.source.network is vyos_defined %}
{% set src = rule_config.source.network %}
{% set src_mask = rule_config.source.inverse_mask %}
{% endif %}
@@ -21,11 +21,11 @@ access-list {{ acl }} remark {{ acl_config.description }}
{% if (acl|int >= 100 and acl|int <= 199) or (acl|int >= 2000 and acl|int <= 2699) %}
{% set ip = 'ip' %}
{% set dst = 'any' %}
-{% if rule_config.destination is defined and rule_config.destination.any is defined %}
+{% if rule_config.destination.any is vyos_defined %}
{% set dst = 'any' %}
-{% elif rule_config.destination is defined and rule_config.destination.host is defined and rule_config.destination.host is not none %}
-{% set dst = 'host ' + rule_config.destination.host %}
-{% elif rule_config.destination is defined and rule_config.destination.network is defined and rule_config.destination.network is not none %}
+{% elif rule_config.destination.host is vyos_defined %}
+{% set dst = 'host ' ~ rule_config.destination.host %}
+{% elif rule_config.destination.network is vyos_defined %}
{% set dst = rule_config.destination.network %}
{% set dst_mask = rule_config.destination.inverse_mask %}
{% endif %}
@@ -36,28 +36,28 @@ access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ ip }} {{ src }}
{% endfor %}
{% endif %}
!
-{% if access_list6 is defined and access_list6 is not none %}
+{% if access_list6 is vyos_defined %}
{% for acl, acl_config in access_list6.items() | natural_sort %}
-{% if acl_config.description is defined and acl_config.description is not none %}
+{% if acl_config.description is vyos_defined %}
ipv6 access-list {{ acl }} remark {{ acl_config.description }}
{% endif %}
-{% if acl_config.rule is defined and acl_config.rule is not none %}
+{% if acl_config.rule is vyos_defined %}
{% for rule, rule_config in acl_config.rule.items() | natural_sort %}
{% set src = '' %}
-{% if rule_config.source is defined and rule_config.source.any is defined %}
+{% if rule_config.source.any is vyos_defined %}
{% set src = 'any' %}
-{% elif rule_config.source is defined and rule_config.source.network is defined and rule_config.source.network is not none %}
+{% elif rule_config.source.network is vyos_defined %}
{% set src = rule_config.source.network %}
{% endif %}
-ipv6 access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ src }} {{ 'exact-match' if rule_config.source.exact_match is defined }}
+ipv6 access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ src }} {{ 'exact-match' if rule_config.source.exact_match is vyos_defined }}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
!
-{% if as_path_list is defined and as_path_list is not none %}
+{% if as_path_list is vyos_defined %}
{% for acl, acl_config in as_path_list.items() | natural_sort %}
-{% if acl_config.rule is defined and acl_config.rule is not none %}
+{% if acl_config.rule is vyos_defined %}
{% for rule, rule_config in acl_config.rule.items() | natural_sort %}
bgp as-path access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.regex }}
{% endfor %}
@@ -65,9 +65,9 @@ bgp as-path access-list {{ acl }} seq {{ rule }} {{ rule_config.action }} {{ rul
{% endfor %}
{% endif %}
!
-{% if community_list is defined and community_list is not none %}
+{% if community_list is vyos_defined %}
{% for list, list_config in community_list.items() | natural_sort %}
-{% if list_config.rule is defined and list_config.rule is not none %}
+{% if list_config.rule is vyos_defined %}
{% for rule, rule_config in list_config.rule.items() | natural_sort %}
{# by default, if casting to int fails it returns 0 #}
{% if list|int != 0 %}
@@ -80,9 +80,9 @@ bgp community-list expanded {{ list }} seq {{ rule }} {{ rule_config.action }} {
{% endfor %}
{% endif %}
!
-{% if extcommunity_list is defined and extcommunity_list is not none %}
+{% if extcommunity_list is vyos_defined %}
{% for list, list_config in extcommunity_list.items() | natural_sort %}
-{% if list_config.rule is defined and list_config.rule is not none %}
+{% if list_config.rule is vyos_defined %}
{% for rule, rule_config in list_config.rule.items() | natural_sort %}
{# by default, if casting to int fails it returns 0 #}
{% if list|int != 0 %}
@@ -95,9 +95,9 @@ bgp extcommunity-list expanded {{ list }} seq {{ rule }} {{ rule_config.action }
{% endfor %}
{% endif %}
!
-{% if large_community_list is defined and large_community_list is not none %}
+{% if large_community_list is vyos_defined %}
{% for list, list_config in large_community_list.items() | natural_sort %}
-{% if list_config.rule is defined and list_config.rule is not none %}
+{% if list_config.rule is vyos_defined %}
{% for rule, rule_config in list_config.rule.items() | natural_sort %}
{# by default, if casting to int fails it returns 0 #}
{% if list|int != 0 %}
@@ -110,206 +110,207 @@ bgp large-community-list expanded {{ list }} seq {{ rule }} {{ rule_config.actio
{% endfor %}
{% endif %}
!
-{% if prefix_list is defined and prefix_list is not none %}
+{% if prefix_list is vyos_defined %}
{% for prefix_list, prefix_list_config in prefix_list.items() | natural_sort %}
-{% if prefix_list_config.description is defined and prefix_list_config.description is not none %}
+{% if prefix_list_config.description is vyos_defined %}
ip prefix-list {{ prefix_list }} description {{ prefix_list_config.description }}
{% endif %}
-{% if prefix_list_config.rule is defined and prefix_list_config.rule is not none %}
+{% if prefix_list_config.rule is vyos_defined %}
{% for rule, rule_config in prefix_list_config.rule.items() | natural_sort %}
-{% if rule_config.prefix is defined and rule_config.prefix is not none %}
-ip prefix-list {{ prefix_list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.prefix }} {{ 'ge ' + rule_config.ge if rule_config.ge is defined }} {{ 'le ' + rule_config.le if rule_config.le is defined }}
+{% if rule_config.prefix is vyos_defined %}
+ip prefix-list {{ prefix_list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.prefix }} {{ 'ge ' ~ rule_config.ge if rule_config.ge is vyos_defined }} {{ 'le ' ~ rule_config.le if rule_config.le is vyos_defined }}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
!
-{% if prefix_list6 is defined and prefix_list6 is not none %}
+{% if prefix_list6 is vyos_defined %}
{% for prefix_list, prefix_list_config in prefix_list6.items() | natural_sort %}
-{% if prefix_list_config.description is defined and prefix_list_config.description is not none %}
+{% if prefix_list_config.description is vyos_defined %}
ipv6 prefix-list {{ prefix_list }} description {{ prefix_list_config.description }}
{% endif %}
-{% if prefix_list_config.rule is defined and prefix_list_config.rule is not none %}
+{% if prefix_list_config.rule is vyos_defined %}
{% for rule, rule_config in prefix_list_config.rule.items() | natural_sort %}
-{% if rule_config.prefix is defined and rule_config.prefix is not none %}
-ipv6 prefix-list {{ prefix_list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.prefix }} {{ 'ge ' + rule_config.ge if rule_config.ge is defined }} {{ 'le ' + rule_config.le if rule_config.le is defined }}
+{% if rule_config.prefix is vyos_defined %}
+ipv6 prefix-list {{ prefix_list }} seq {{ rule }} {{ rule_config.action }} {{ rule_config.prefix }} {{ 'ge ' ~ rule_config.ge if rule_config.ge is vyos_defined }} {{ 'le ' ~ rule_config.le if rule_config.le is vyos_defined }}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
!
-{% if route_map is defined and route_map is not none %}
+{% if route_map is vyos_defined %}
{% for route_map, route_map_config in route_map.items() | natural_sort %}
-{% if route_map_config.rule is defined and route_map_config.rule is not none %}
+{% if route_map_config.rule is vyos_defined %}
{% for rule, rule_config in route_map_config.rule.items() | natural_sort %}
route-map {{ route_map }} {{ rule_config.action }} {{ rule }}
-{% if rule_config.call is defined and rule_config.call is not none %}
+{% if rule_config.call is vyos_defined %}
call {{ rule_config.call }}
{% endif %}
-{% if rule_config.continue is defined and rule_config.continue is not none %}
+{% if rule_config.continue is vyos_defined %}
on-match goto {{ rule_config.continue }}
{% endif %}
-{% if rule_config.description is defined and rule_config.description is not none %}
+{% if rule_config.description is vyos_defined %}
description {{ rule_config.description }}
{% endif %}
-{% if rule_config.match is defined and rule_config.match is not none %}
-{% if rule_config.match.as_path is defined and rule_config.match.as_path is not none %}
+{% if rule_config.match is vyos_defined %}
+{% if rule_config.match.as_path is vyos_defined %}
match as-path {{ rule_config.match.as_path }}
{% endif %}
-{% if rule_config.match.community is defined and rule_config.match.community.community_list is defined and rule_config.match.community.community_list is not none %}
- match community {{ rule_config.match.community.community_list }} {{ 'exact-match' if rule_config.match.community.exact_match is defined }}
+{% if rule_config.match.community.community_list is vyos_defined %}
+ match community {{ rule_config.match.community.community_list }} {{ 'exact-match' if rule_config.match.community.exact_match is vyos_defined }}
{% endif %}
-{% if rule_config.match.extcommunity is defined and rule_config.match.extcommunity is not none %}
+{% if rule_config.match.extcommunity is vyos_defined %}
match extcommunity {{ rule_config.match.extcommunity }}
{% endif %}
-{% if rule_config.match.evpn is defined and rule_config.match.evpn.default_route is defined %}
+{% if rule_config.match.evpn.default_route is vyos_defined %}
match evpn default-route
{% endif %}
-{% if rule_config.match.evpn is defined and rule_config.match.evpn.rd is defined and rule_config.match.evpn.rd is not none %}
+{% if rule_config.match.evpn.rd is vyos_defined %}
match evpn rd {{ rule_config.match.evpn.rd }}
{% endif %}
-{% if rule_config.match.evpn is defined and rule_config.match.evpn.route_type is defined and rule_config.match.evpn.route_type is not none %}
+{% if rule_config.match.evpn.route_type is vyos_defined %}
match evpn route-type {{ rule_config.match.evpn.route_type }}
{% endif %}
-{% if rule_config.match.evpn is defined and rule_config.match.evpn.vni is defined and rule_config.match.evpn.vni is not none %}
+{% if rule_config.match.evpn.vni is vyos_defined %}
match evpn vni {{ rule_config.match.evpn.vni }}
{% endif %}
-{% if rule_config.match.interface is defined and rule_config.match.interface is not none %}
+{% if rule_config.match.interface is vyos_defined %}
match interface {{ rule_config.match.interface }}
{% endif %}
-{% if rule_config.match.ip is defined and rule_config.match.ip.address is defined and rule_config.match.ip.address.access_list is defined and rule_config.match.ip.address.access_list is not none %}
+{% if rule_config.match.ip.address.access_list is vyos_defined %}
match ip address {{ rule_config.match.ip.address.access_list }}
{% endif %}
-{% if rule_config.match.ip is defined and rule_config.match.ip.address is defined and rule_config.match.ip.address.prefix_list is defined and rule_config.match.ip.address.prefix_list is not none %}
+{% if rule_config.match.ip.address.prefix_list is vyos_defined %}
match ip address prefix-list {{ rule_config.match.ip.address.prefix_list }}
{% endif %}
-{% if rule_config.match.ip is defined and rule_config.match.ip.nexthop is defined and rule_config.match.ip.nexthop.access_list is defined and rule_config.match.ip.nexthop.access_list is not none %}
+{% if rule_config.match.ip.nexthop.access_list is vyos_defined %}
match ip next-hop {{ rule_config.match.ip.nexthop.access_list }}
{% endif %}
-{% if rule_config.match.ip is defined and rule_config.match.ip.nexthop is defined and rule_config.match.ip.nexthop.prefix_list is defined and rule_config.match.ip.nexthop.prefix_list is not none %}
+{% if rule_config.match.ip.nexthop.prefix_list is vyos_defined %}
match ip next-hop prefix-list {{ rule_config.match.ip.nexthop.prefix_list }}
{% endif %}
-{% if rule_config.match.ip is defined and rule_config.match.ip.route_source is defined and rule_config.match.ip.route_source.access_list is defined and rule_config.match.ip.route_source.access_list is not none %}
+{% if rule_config.match.ip.route_source.access_list is vyos_defined %}
match ip route-source {{ rule_config.match.ip.route_source.access_list }}
{% endif %}
-{% if rule_config.match.ip is defined and rule_config.match.ip.route_source is defined and rule_config.match.ip.route_source.prefix_list is defined and rule_config.match.ip.route_source.prefix_list is not none %}
+{% if rule_config.match.ip.route_source.prefix_list is vyos_defined %}
match ip route-source prefix-list {{ rule_config.match.ip.route_source.prefix_list }}
{% endif %}
-{% if rule_config.match.ipv6 is defined and rule_config.match.ipv6.address is defined and rule_config.match.ipv6.address.access_list is defined and rule_config.match.ipv6.address.access_list is not none %}
+{% if rule_config.match.ipv6.address.access_list is vyos_defined %}
match ipv6 address {{ rule_config.match.ipv6.address.access_list }}
{% endif %}
-{% if rule_config.match.ipv6 is defined and rule_config.match.ipv6.address is defined and rule_config.match.ipv6.address.prefix_list is defined and rule_config.match.ipv6.address.prefix_list is not none %}
+{% if rule_config.match.ipv6.address.prefix_list is vyos_defined %}
match ipv6 address prefix-list {{ rule_config.match.ipv6.address.prefix_list }}
{% endif %}
-{% if rule_config.match.ipv6 is defined and rule_config.match.ipv6.nexthop is defined and rule_config.match.ipv6.nexthop is not none %}
- match ipv6 next-hop {{ rule_config.match.ipv6.nexthop }}
+{% if rule_config.match.ipv6.nexthop is vyos_defined %}
+ match ipv6 next-hop address {{ rule_config.match.ipv6.nexthop }}
{% endif %}
-{% if rule_config.match.large_community is defined and rule_config.match.large_community.large_community_list is defined and rule_config.match.large_community.large_community_list is not none %}
+{% if rule_config.match.large_community.large_community_list is vyos_defined %}
match large-community {{ rule_config.match.large_community.large_community_list }}
{% endif %}
-{% if rule_config.match.local_preference is defined and rule_config.match.local_preference is not none %}
+{% if rule_config.match.local_preference is vyos_defined %}
match local-preference {{ rule_config.match.local_preference }}
{% endif %}
-{% if rule_config.match.metric is defined and rule_config.match.metric is not none %}
+{% if rule_config.match.metric is vyos_defined %}
match metric {{ rule_config.match.metric }}
{% endif %}
-{% if rule_config.match.origin is defined and rule_config.match.origin is not none %}
+{% if rule_config.match.origin is vyos_defined %}
match origin {{ rule_config.match.origin }}
{% endif %}
-{% if rule_config.match.peer is defined and rule_config.match.peer is not none %}
+{% if rule_config.match.peer is vyos_defined %}
match peer {{ rule_config.match.peer }}
{% endif %}
-{% if rule_config.match.rpki is defined and rule_config.match.rpki is not none %}
+{% if rule_config.match.rpki is vyos_defined %}
match rpki {{ rule_config.match.rpki }}
{% endif %}
-{% if rule_config.match.tag is defined and rule_config.match.tag is not none %}
+{% if rule_config.match.tag is vyos_defined %}
match tag {{ rule_config.match.tag }}
{% endif %}
{% endif %}
-{% if rule_config.on_match is defined and rule_config.on_match is not none %}
-{% if rule_config.on_match.next is defined %}
+{% if rule_config.on_match.next is vyos_defined %}
on-match next
-{% endif %}
-{% if rule_config.on_match.goto is defined and rule_config.on_match.goto is not none %}
+{% endif %}
+{% if rule_config.on_match.goto is vyos_defined %}
on-match goto {{ rule_config.on_match.goto }}
-{% endif %}
{% endif %}
-{% if rule_config.set is defined and rule_config.set is not none %}
-{% if rule_config.set.aggregator is defined and rule_config.set.aggregator.as is defined and rule_config.set.aggregator.ip is defined %}
+{% if rule_config.set is vyos_defined %}
+{% if rule_config.set.aggregator.as is vyos_defined and rule_config.set.aggregator.ip is vyos_defined %}
set aggregator as {{ rule_config.set.aggregator.as }} {{ rule_config.set.aggregator.ip }}
{% endif %}
-{% if rule_config.set.as_path_exclude is defined and rule_config.set.as_path_exclude is not none %}
+{% if rule_config.set.as_path_exclude is vyos_defined %}
set as-path exclude {{ rule_config.set.as_path_exclude }}
{% endif %}
-{% if rule_config.set.as_path_prepend is defined and rule_config.set.as_path_prepend is not none %}
+{% if rule_config.set.as_path_prepend is vyos_defined %}
set as-path prepend {{ rule_config.set.as_path_prepend }}
{% endif %}
-{% if rule_config.set.atomic_aggregate is defined %}
+{% if rule_config.set.atomic_aggregate is vyos_defined %}
set atomic-aggregate
{% endif %}
-{% if rule_config.set.comm_list is defined and rule_config.set.comm_list.comm_list is defined and rule_config.set.comm_list.comm_list is not none %}
- set comm-list {{ rule_config.set.comm_list.comm_list }} {{ 'delete' if rule_config.set.comm_list.delete is defined }}
+{% if rule_config.set.comm_list.comm_list is vyos_defined %}
+ set comm-list {{ rule_config.set.comm_list.comm_list }} {{ 'delete' if rule_config.set.comm_list.delete is vyos_defined }}
{% endif %}
-{% if rule_config.set.community is defined and rule_config.set.community is not none %}
+{% if rule_config.set.community is vyos_defined %}
set community {{ rule_config.set.community }}
{% endif %}
-{% if rule_config.set.distance is defined and rule_config.set.distance is not none %}
+{% if rule_config.set.distance is vyos_defined %}
set distance {{ rule_config.set.distance }}
{% endif %}
-{% if rule_config.set.extcommunity is defined and rule_config.set.extcommunity.bandwidth is defined and rule_config.set.extcommunity.bandwidth is not none %}
+{% if rule_config.set.extcommunity.bandwidth is vyos_defined %}
set extcommunity bandwidth {{ rule_config.set.extcommunity.bandwidth }}
{% endif %}
-{% if rule_config.set.extcommunity is defined and rule_config.set.extcommunity.rt is defined and rule_config.set.extcommunity.rt is not none %}
+{% if rule_config.set.extcommunity.rt is vyos_defined %}
set extcommunity rt {{ rule_config.set.extcommunity.rt }}
{% endif %}
-{% if rule_config.set.extcommunity is defined and rule_config.set.extcommunity.soo is defined and rule_config.set.extcommunity.soo is not none %}
+{% if rule_config.set.extcommunity.soo is vyos_defined %}
set extcommunity soo {{ rule_config.set.extcommunity.soo }}
{% endif %}
-{% if rule_config.set.ip_next_hop is defined and rule_config.set.ip_next_hop is not none %}
+{% if rule_config.set.ip_next_hop is vyos_defined %}
set ip next-hop {{ rule_config.set.ip_next_hop }}
{% endif %}
-{% if rule_config.set.ipv6_next_hop is defined and rule_config.set.ipv6_next_hop.global is defined and rule_config.set.ipv6_next_hop.global is not none %}
+{% if rule_config.set.ipv6_next_hop.global is vyos_defined %}
set ipv6 next-hop global {{ rule_config.set.ipv6_next_hop.global }}
{% endif %}
-{% if rule_config.set.ipv6_next_hop is defined and rule_config.set.ipv6_next_hop.local is defined and rule_config.set.ipv6_next_hop.local is not none %}
+{% if rule_config.set.ipv6_next_hop.local is vyos_defined %}
set ipv6 next-hop local {{ rule_config.set.ipv6_next_hop.local }}
{% endif %}
-{% if rule_config.set.ipv6_next_hop is defined and rule_config.set.ipv6_next_hop.prefer_global is defined %}
+{% if rule_config.set.ipv6_next_hop.peer_address is vyos_defined %}
+ set ipv6 next-hop peer-address
+{% endif %}
+{% if rule_config.set.ipv6_next_hop.prefer_global is vyos_defined %}
set ipv6 next-hop prefer-global
{% endif %}
-{% if rule_config.set.large_community is defined and rule_config.set.large_community is not none %}
+{% if rule_config.set.large_community is vyos_defined %}
set large-community {{ rule_config.set.large_community }}
{% endif %}
-{% if rule_config.set.large_comm_list_delete is defined and rule_config.set.large_comm_list_delete is not none %}
+{% if rule_config.set.large_comm_list_delete is vyos_defined %}
set large-comm-list {{ rule_config.set.large_comm_list_delete }} delete
{% endif %}
-{% if rule_config.set.local_preference is defined and rule_config.set.local_preference is not none %}
+{% if rule_config.set.local_preference is vyos_defined %}
set local-preference {{ rule_config.set.local_preference }}
{% endif %}
-{% if rule_config.set.metric is defined and rule_config.set.metric is not none %}
+{% if rule_config.set.metric is vyos_defined %}
set metric {{ rule_config.set.metric }}
{% endif %}
-{% if rule_config.set.metric_type is defined and rule_config.set.metric_type is not none %}
+{% if rule_config.set.metric_type is vyos_defined %}
set metric-type {{ rule_config.set.metric_type }}
{% endif %}
-{% if rule_config.set.origin is defined and rule_config.set.origin is not none %}
+{% if rule_config.set.origin is vyos_defined %}
set origin {{ rule_config.set.origin }}
{% endif %}
-{% if rule_config.set.originator_id is defined and rule_config.set.originator_id is not none %}
+{% if rule_config.set.originator_id is vyos_defined %}
set originator-id {{ rule_config.set.originator_id }}
{% endif %}
-{% if rule_config.set.src is defined and rule_config.set.src is not none %}
+{% if rule_config.set.src is vyos_defined %}
set src {{ rule_config.set.src }}
{% endif %}
-{% if rule_config.set.table is defined and rule_config.set.table is not none %}
+{% if rule_config.set.table is vyos_defined %}
set table {{ rule_config.set.table }}
{% endif %}
-{% if rule_config.set.tag is defined and rule_config.set.tag is not none %}
+{% if rule_config.set.tag is vyos_defined %}
set tag {{ rule_config.set.tag }}
{% endif %}
-{% if rule_config.set.weight is defined and rule_config.set.weight is not none %}
+{% if rule_config.set.weight is vyos_defined %}
set weight {{ rule_config.set.weight }}
{% endif %}
{% endif %}
diff --git a/data/templates/frr/rip_ripng.frr.j2 b/data/templates/frr/rip_ripng.frr.j2
index de180ee6b..3732371b2 100644
--- a/data/templates/frr/rip_ripng.frr.j2
+++ b/data/templates/frr/rip_ripng.frr.j2
@@ -1,36 +1,36 @@
-{% if default_information is defined and default_information.originate is defined %}
+{% if default_information is vyos_defined %}
default-information originate
{% endif %}
-{% if default_metric is defined and default_metric is not none %}
+{% if default_metric is vyos_defined %}
default-metric {{ default_metric }}
{% endif %}
-{% if passive_interface is defined and passive_interface is not none %}
+{% if passive_interface is vyos_defined %}
{% for interface in passive_interface %}
passive-interface {{ interface }}
{% endfor %}
{% endif %}
-{% if network is defined and network is not none %}
+{% if network is vyos_defined %}
{% for prefix in network %}
network {{ prefix }}
{% endfor %}
{% endif %}
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for ifname in interface %}
network {{ ifname }}
{% endfor %}
{% endif %}
-{% if route is defined and route is not none %}
+{% if route is vyos_defined %}
{% for prefix in route %}
route {{ prefix }}
{% endfor %}
{% endif %}
{# timers have default values #}
timers basic {{ timers['update'] }} {{ timers.timeout }} {{ timers.garbage_collection }}
-{% if redistribute is defined and redistribute is not none %}
+{% if redistribute is vyos_defined %}
{% for protocol, protocol_config in redistribute.items() %}
-{% if protocol == 'ospfv3' %}
+{% if protocol is vyos_defined('ospfv3') %}
{% set protocol = 'ospf6' %}
{% endif %}
- redistribute {{ protocol }} {{ 'metric ' + protocol_config.metric if protocol_config.metric is defined }} {{ 'route-map ' + protocol_config.route_map if protocol_config.route_map is defined }}
+ redistribute {{ protocol }} {{ 'metric ' ~ protocol_config.metric if protocol_config.metric is vyos_defined }} {{ 'route-map ' ~ protocol_config.route_map if protocol_config.route_map is vyos_defined }}
{% endfor %}
{% endif %}
diff --git a/data/templates/frr/ripd.frr.tmpl b/data/templates/frr/ripd.frr.tmpl
index c44bb6d27..2dbb93052 100644
--- a/data/templates/frr/ripd.frr.tmpl
+++ b/data/templates/frr/ripd.frr.tmpl
@@ -1,11 +1,11 @@
{# RIP key-chain definition #}
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
-{% if iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %}
+{% if iface_config.authentication.md5 is vyos_defined %}
key chain {{ iface }}-rip
{% for key_id, key_options in iface_config.authentication.md5.items() %}
key {{ key_id }}
-{% if key_options.password is defined and key_options.password is not none %}
+{% if key_options.password is vyos_defined %}
key-string {{ key_options.password }}
{% endif %}
exit
@@ -16,20 +16,20 @@ exit
{% endif %}
!
{# Interface specific configuration #}
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
interface {{ iface }}
-{% if iface_config.authentication is defined and iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %}
+{% if iface_config.authentication.plaintext_password is vyos_defined %}
ip rip authentication mode text
ip rip authentication string {{ iface_config.authentication.plaintext_password }}
-{% elif iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %}
+{% elif iface_config.authentication.md5 is vyos_defined %}
ip rip authentication key-chain {{ iface }}-rip
ip rip authentication mode md5
{% endif %}
-{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %}
+{% if iface_config.split_horizon.disable is vyos_defined %}
no ip rip split-horizon
{% endif %}
-{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %}
+{% if iface_config.split_horizon.poison_reverse is vyos_defined %}
ip rip split-horizon poisoned-reverse
{% endif %}
exit
@@ -38,63 +38,55 @@ exit
{% endif %}
!
router rip
-{% if default_distance is defined and default_distance is not none %}
+{% if default_distance is vyos_defined %}
distance {{ default_distance }}
{% endif %}
-{% if network_distance is defined and network_distance is not none %}
+{% if network_distance is vyos_defined %}
{% for network, network_config in network_distance.items() %}
-{% if network_config.distance is defined and network_config.distance is not none %}
+{% if network_config.distance is vyos_defined %}
distance {{ network_config.distance }} {{ network }}
{% endif %}
{% endfor %}
{% endif %}
-{% if neighbor is defined and neighbor is not none %}
+{% if neighbor is vyos_defined %}
{% for address in neighbor %}
neighbor {{ address }}
{% endfor %}
{% endif %}
-{% if distribute_list is defined and distribute_list is not none %}
-{% if distribute_list.access_list is defined and distribute_list.access_list is not none %}
-{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %}
+{% if distribute_list is vyos_defined %}
+{% if distribute_list.access_list.in is vyos_defined %}
distribute-list {{ distribute_list.access_list.in }} in
-{% endif %}
-{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %}
+{% endif %}
+{% if distribute_list.access_list.out is vyos_defined %}
distribute-list {{ distribute_list.access_list.out }} out
-{% endif %}
{% endif %}
-{% if distribute_list.interface is defined and distribute_list.interface is not none %}
+{% if distribute_list.interface is vyos_defined %}
{% for interface, interface_config in distribute_list.interface.items() %}
-{% if interface_config.access_list is defined and interface_config.access_list is not none %}
-{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %}
+{% if interface_config.access_list.in is vyos_defined %}
distribute-list {{ interface_config.access_list.in }} in {{ interface }}
-{% endif %}
-{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %}
+{% endif %}
+{% if interface_config.access_list.out is vyos_defined %}
distribute-list {{ interface_config.access_list.out }} out {{ interface }}
-{% endif %}
{% endif %}
-{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %}
-{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %}
+{% if interface_config.prefix_list.in is vyos_defined %}
distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }}
-{% endif %}
-{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %}
+{% endif %}
+{% if interface_config.prefix_list.out is vyos_defined %}
distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }}
-{% endif %}
{% endif %}
{% endfor %}
{% endif %}
-{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %}
-{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %}
+{% if distribute_list.prefix_list.in is vyos_defined %}
distribute-list prefix {{ distribute_list.prefix_list.in }} in
-{% endif %}
-{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %}
+{% endif %}
+{% if distribute_list.prefix_list.out is vyos_defined %}
distribute-list prefix {{ distribute_list.prefix_list.out }} out
-{% endif %}
{% endif %}
{% endif %}
{% include 'frr/rip_ripng.frr.j2' %}
exit
!
-{% if route_map is defined and route_map is not none %}
+{% if route_map is vyos_defined %}
ip protocol rip route-map {{ route_map }}
{% endif %}
!
diff --git a/data/templates/frr/ripngd.frr.tmpl b/data/templates/frr/ripngd.frr.tmpl
index ca7b9b5fb..06c61dd48 100644
--- a/data/templates/frr/ripngd.frr.tmpl
+++ b/data/templates/frr/ripngd.frr.tmpl
@@ -1,11 +1,11 @@
{# Interface specific configuration #}
-{% if interface is defined and interface is not none %}
+{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
interface {{ iface }}
-{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %}
+{% if iface_config.split_horizon.disable is vyos_defined %}
no ipv6 rip split-horizon
{% endif %}
-{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %}
+{% if iface_config.split_horizon.poison_reverse is vyos_defined %}
ipv6 rip split-horizon poisoned-reverse
{% endif %}
exit
@@ -13,53 +13,45 @@ exit
{% endif %}
!
router ripng
-{% if aggregate_address is defined and aggregate_address is not none %}
+{% if aggregate_address is vyos_defined %}
{% for prefix in aggregate_address %}
aggregate-address {{ prefix }}
{% endfor %}
{% endif %}
-{% if distribute_list is defined and distribute_list is not none %}
-{% if distribute_list.access_list is defined and distribute_list.access_list is not none %}
-{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %}
+{% if distribute_list is vyos_defined %}
+{% if distribute_list.access_list.in is vyos_defined %}
ipv6 distribute-list {{ distribute_list.access_list.in }} in
-{% endif %}
-{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %}
+{% endif %}
+{% if distribute_list.access_list.out is vyos_defined %}
ipv6 distribute-list {{ distribute_list.access_list.out }} out
-{% endif %}
{% endif %}
-{% if distribute_list.interface is defined and distribute_list.interface is not none %}
+{% if distribute_list.interface is vyos_defined %}
{% for interface, interface_config in distribute_list.interface.items() %}
-{% if interface_config.access_list is defined and interface_config.access_list is not none %}
-{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %}
+{% if interface_config.access_list.in is vyos_defined %}
ipv6 distribute-list {{ interface_config.access_list.in }} in {{ interface }}
-{% endif %}
-{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %}
+{% endif %}
+{% if interface_config.access_list.out is vyos_defined %}
ipv6 distribute-list {{ interface_config.access_list.out }} out {{ interface }}
-{% endif %}
{% endif %}
-{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %}
-{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %}
+{% if interface_config.prefix_list.in is vyos_defined %}
ipv6 distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }}
-{% endif %}
-{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %}
+{% endif %}
+{% if interface_config.prefix_list.out is vyos_defined %}
ipv6 distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }}
-{% endif %}
{% endif %}
{% endfor %}
{% endif %}
-{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %}
-{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %}
+{% if distribute_list.prefix_list.in is vyos_defined %}
ipv6 distribute-list prefix {{ distribute_list.prefix_list.in }} in
-{% endif %}
-{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %}
+{% endif %}
+{% if distribute_list.prefix_list.out is vyos_defined %}
ipv6 distribute-list prefix {{ distribute_list.prefix_list.out }} out
-{% endif %}
{% endif %}
{% endif %}
{% include 'frr/rip_ripng.frr.j2' %}
exit
!
-{% if route_map is defined and route_map is not none %}
+{% if route_map is vyos_defined %}
ipv6 protocol ripng route-map {{ route_map }}
{% endif %}
!
diff --git a/data/templates/frr/rpki.frr.tmpl b/data/templates/frr/rpki.frr.tmpl
index 7f9823f6b..3f4fd3236 100644
--- a/data/templates/frr/rpki.frr.tmpl
+++ b/data/templates/frr/rpki.frr.tmpl
@@ -1,17 +1,17 @@
!
{# as FRR does not support deleting the entire rpki section we leave it in place even when it's empty #}
rpki
-{% if cache is defined and cache is not none %}
+{% if cache is vyos_defined %}
{% for peer, peer_config in cache.items() %}
{# port is mandatory and preference uses a default value #}
-{% if peer_config.ssh is defined and peer_config.ssh.username is defined and peer_config.ssh.username is not none %}
+{% if peer_config.ssh.username is vyos_defined %}
rpki cache {{ peer | replace('_', '-') }} {{ peer_config.port }} {{ peer_config.ssh.username }} {{ peer_config.ssh.private_key_file }} {{ peer_config.ssh.public_key_file }} {{ peer_config.ssh.known_hosts_file }} preference {{ peer_config.preference }}
{% else %}
rpki cache {{ peer | replace('_', '-') }} {{ peer_config.port }} preference {{ peer_config.preference }}
{% endif %}
{% endfor %}
{% endif %}
-{% if polling_period is defined and polling_period is not none %}
+{% if polling_period is vyos_defined %}
rpki polling_period {{ polling_period }}
{% endif %}
exit
diff --git a/data/templates/frr/static_routes_macro.j2 b/data/templates/frr/static_routes_macro.j2
index 3b432b49b..0b242a868 100644
--- a/data/templates/frr/static_routes_macro.j2
+++ b/data/templates/frr/static_routes_macro.j2
@@ -1,21 +1,24 @@
{% macro static_routes(ip_ipv6, prefix, prefix_config, table=None) %}
-{% if prefix_config.blackhole is defined %}
-{{ ip_ipv6 }} route {{ prefix }} blackhole {{ prefix_config.blackhole.distance if prefix_config.blackhole.distance is defined }} {{ 'tag ' + prefix_config.blackhole.tag if prefix_config.blackhole.tag is defined }} {{ 'table ' + table if table is defined and table is not none }}
+{% if prefix_config.blackhole is vyos_defined %}
+{{ ip_ipv6 }} route {{ prefix }} blackhole {{ prefix_config.blackhole.distance if prefix_config.blackhole.distance is vyos_defined }} {{ 'tag ' ~ prefix_config.blackhole.tag if prefix_config.blackhole.tag is vyos_defined }} {{ 'table ' ~ table if table is vyos_defined and table is not none }}
{% endif %}
-{% if prefix_config.dhcp_interface is defined and prefix_config.dhcp_interface is not none %}
+{% if prefix_config.reject is vyos_defined %}
+{{ ip_ipv6 }} route {{ prefix }} reject {{ prefix_config.reject.distance if prefix_config.reject.distance is vyos_defined }} {{ 'tag ' ~ prefix_config.reject.tag if prefix_config.reject.tag is vyos_defined }} {{ 'table ' ~ table if table is vyos_defined }}
+{% endif %}
+{% if prefix_config.dhcp_interface is vyos_defined %}
{% set next_hop = prefix_config.dhcp_interface | get_dhcp_router %}
-{% if next_hop is defined and next_hop is not none %}
-{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ prefix_config.dhcp_interface }}
+{% if next_hop is vyos_defined %}
+{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ prefix_config.dhcp_interface }} {{ 'table ' ~ table if table is vyos_defined }}
{% endif %}
{% endif %}
-{% if prefix_config.interface is defined and prefix_config.interface is not none %}
+{% if prefix_config.interface is vyos_defined %}
{% for interface, interface_config in prefix_config.interface.items() if interface_config.disable is not defined %}
-{{ ip_ipv6 }} route {{ prefix }} {{ interface }} {{ interface_config.distance if interface_config.distance is defined }} {{ 'nexthop-vrf ' + interface_config.vrf if interface_config.vrf is defined }} {{ 'table ' + table if table is defined and table is not none }}
+{{ ip_ipv6 }} route {{ prefix }} {{ interface }} {{ interface_config.distance if interface_config.distance is vyos_defined }} {{ 'nexthop-vrf ' ~ interface_config.vrf if interface_config.vrf is vyos_defined }} {{ 'table ' ~ table if table is vyos_defined }}
{% endfor %}
{% endif %}
-{% if prefix_config.next_hop is defined and prefix_config.next_hop is not none %}
+{% if prefix_config.next_hop is vyos_defined and prefix_config.next_hop is not none %}
{% for next_hop, next_hop_config in prefix_config.next_hop.items() if next_hop_config.disable is not defined %}
-{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ next_hop_config.interface if next_hop_config.interface is defined }} {{ next_hop_config.distance if next_hop_config.distance is defined }} {{ 'nexthop-vrf ' + next_hop_config.vrf if next_hop_config.vrf is defined }} {{ 'table ' + table if table is defined and table is not none }}
+{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ next_hop_config.interface if next_hop_config.interface is vyos_defined }} {{ next_hop_config.distance if next_hop_config.distance is vyos_defined }} {{ 'nexthop-vrf ' ~ next_hop_config.vrf if next_hop_config.vrf is vyos_defined }} {{ 'table ' ~ table if table is vyos_defined}}
{% endfor %}
{% endif %}
{% endmacro %}
diff --git a/data/templates/frr/staticd.frr.tmpl b/data/templates/frr/staticd.frr.tmpl
index bfe959c1d..c7138b12b 100644
--- a/data/templates/frr/staticd.frr.tmpl
+++ b/data/templates/frr/staticd.frr.tmpl
@@ -2,7 +2,7 @@
!
{% set ip_prefix = 'ip' %}
{% set ipv6_prefix = 'ipv6' %}
-{% if vrf is defined and vrf is not none %}
+{% if vrf is vyos_defined %}
{# We need to add an additional whitespace in front of the prefix #}
{# when VRFs are in use, thus we use a variable for prefix handling #}
{% set ip_prefix = ' ip' %}
@@ -10,40 +10,40 @@
vrf {{ vrf }}
{% endif %}
{# IPv4 routing #}
-{% if route is defined and route is not none %}
+{% if route is vyos_defined %}
{% for prefix, prefix_config in route.items() %}
{{ static_routes(ip_prefix, prefix, prefix_config) }}
{%- endfor -%}
{% endif %}
{# IPv4 default routes from DHCP interfaces #}
-{% if dhcp is defined and dhcp is not none %}
-{% for interface in dhcp %}
+{% if dhcp is vyos_defined %}
+{% for interface, interface_config in dhcp.items() %}
{% set next_hop = interface | get_dhcp_router %}
-{% if next_hop is defined and next_hop is not none %}
-{{ ip_prefix }} route 0.0.0.0/0 {{ next_hop }} {{ interface }} tag 210 210
+{% if next_hop is vyos_defined %}
+{{ ip_prefix }} route 0.0.0.0/0 {{ next_hop }} {{ interface }} tag 210 {{ interface_config.distance }}
{% endif %}
{% endfor %}
{% endif %}
{# IPv6 routing #}
-{% if route6 is defined and route6 is not none %}
+{% if route6 is vyos_defined %}
{% for prefix, prefix_config in route6.items() %}
{{ static_routes(ipv6_prefix, prefix, prefix_config) }}
{%- endfor -%}
{% endif %}
-{% if vrf is defined and vrf is not none %}
+{% if vrf is vyos_defined %}
exit-vrf
{% endif %}
!
{# Policy route tables #}
-{% if table is defined and table is not none %}
+{% if table is vyos_defined %}
{% for table_id, table_config in table.items() %}
-{% if table_config.route is defined and table_config.route is not none %}
+{% if table_config.route is vyos_defined %}
{% for prefix, prefix_config in table_config.route.items() %}
{{ static_routes('ip', prefix, prefix_config, table_id) }}
{%- endfor -%}
{% endif %}
!
-{% if table_config.route6 is defined and table_config.route6 is not none %}
+{% if table_config.route6 is vyos_defined %}
{% for prefix, prefix_config in table_config.route6.items() %}
{{ static_routes('ipv6', prefix, prefix_config, table_id) }}
{%- endfor -%}
@@ -52,7 +52,7 @@ vrf {{ vrf }}
{% endfor %}
{% endif %}
!
-{% if route_map is defined and route_map is not none %}
+{% if route_map is vyos_defined %}
ip protocol static route-map {{ route_map }}
!
{% endif %}
diff --git a/data/templates/frr/vrf-vni.frr.tmpl b/data/templates/frr/vrf-vni.frr.tmpl
index 299c9719e..916b5d05d 100644
--- a/data/templates/frr/vrf-vni.frr.tmpl
+++ b/data/templates/frr/vrf-vni.frr.tmpl
@@ -1,7 +1,7 @@
-{% if name is defined and name is not none %}
+{% if name is vyos_defined %}
{% for vrf, vrf_config in name.items() %}
vrf {{ vrf }}
-{% if vrf_config.vni is defined and vrf_config.vni is not none %}
+{% if vrf_config.vni is vyos_defined %}
vni {{ vrf_config.vni }}
{% endif %}
exit-vrf
diff --git a/data/templates/frr/vrf.route-map.frr.tmpl b/data/templates/frr/vrf.route-map.frr.tmpl
index cb0e07616..5e0c56a7b 100644
--- a/data/templates/frr/vrf.route-map.frr.tmpl
+++ b/data/templates/frr/vrf.route-map.frr.tmpl
@@ -1,10 +1,10 @@
!
-{% if vrf is defined and vrf is not none and route_map is defined and route_map is not none %}
+{% if vrf is vyos_defined and route_map is vyos_defined %}
vrf {{ vrf }}
ip protocol {{ protocol }} route-map {{ route_map }}
exit-vrf
!
-{% elif route_map is defined and route_map is not none %}
+{% elif route_map is vyos_defined %}
ip protocol {{ protocol }} route-map {{ route_map }}
{% endif %}
!
diff --git a/data/templates/high-availability/keepalived.conf.tmpl b/data/templates/high-availability/keepalived.conf.tmpl
index afd5f5383..68c707f17 100644
--- a/data/templates/high-availability/keepalived.conf.tmpl
+++ b/data/templates/high-availability/keepalived.conf.tmpl
@@ -28,6 +28,9 @@ vrrp_instance {{ name }} {
virtual_router_id {{ group_config.vrid }}
priority {{ group_config.priority }}
advert_int {{ group_config.advertise_interval }}
+{% if group_config.track is defined and group_config.track.exclude_vrrp_interface is defined %}
+ dont_track_primary
+{% endif %}
{% 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 %}
@@ -61,8 +64,8 @@ vrrp_instance {{ name }} {
{% endif %}
{% if group_config.address is defined and group_config.address is not none %}
virtual_ipaddress {
-{% for addr in group_config.address %}
- {{ addr }}
+{% for addr, addr_config in group_config.address.items() %}
+ {{ addr }}{{ ' dev ' + addr_config.interface if addr_config.interface is defined }}
{% endfor %}
}
{% endif %}
@@ -73,6 +76,13 @@ vrrp_instance {{ name }} {
{% endfor %}
}
{% endif %}
+{% if group_config.track is defined and group_config.track.interface is defined and group_config.track.interface is not none %}
+ track_interface {
+{% for interface in group_config.track.interface %}
+ {{ interface }}
+{% endfor %}
+ }
+{% 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_{{ name }}
@@ -103,7 +113,7 @@ vrrp_sync_group {{ name }} {
{% endif %}
{% endfor %}
{% endif %}
-{% if vrrp.conntrack_sync_group is defined and vrrp.conntrack_sync_group == name %}
+{% 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 }}"
@@ -113,8 +123,8 @@ vrrp_sync_group {{ name }} {
{% endfor %}
{% endif %}
-# Virtual-server configuration
{% if virtual_server is defined and virtual_server is not none %}
+# Virtual-server configuration
{% for vserver, vserver_config in virtual_server.items() %}
virtual_server {{ vserver }} {{ vserver_config.port }} {
delay_loop {{ vserver_config.delay_loop }}
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
index e8511bd62..a51505270 100644
--- a/data/templates/https/nginx.default.tmpl
+++ b/data/templates/https/nginx.default.tmpl
@@ -53,19 +53,6 @@ server {
}
error_page 497 =301 https://$host:{{ server.port }}$request_uri;
- error_page 501 502 503 =200 @50*_json;
-
-{% if api_set %}
- location @50*_json {
- default_type application/json;
- return 200 '{"error": "service https api unavailable at this proxy address: set service https api-restrict virtual-host"}';
- }
-{% else %}
- location @50*_json {
- default_type application/json;
- return 200 '{"error": "Start service in configuration mode: set service https api"}';
- }
-{% endif %}
}
diff --git a/data/templates/ipsec/charon.tmpl b/data/templates/ipsec/charon.tmpl
index 4d710921e..b9b020dcd 100644
--- a/data/templates/ipsec/charon.tmpl
+++ b/data/templates/ipsec/charon.tmpl
@@ -20,6 +20,17 @@ charon {
# Send Cisco Unity vendor ID payload (IKEv1 only).
# cisco_unity = no
+ # Cisco FlexVPN
+{% if options is defined %}
+ cisco_flexvpn = {{ 'yes' if options.flexvpn is defined else 'no' }}
+{% if options.virtual_ip is defined %}
+ install_virtual_ip = yes
+{% endif %}
+{% if options.interface is defined and options.interface is not none %}
+ install_virtual_ip_on = {{ options.interface }}
+{% endif %}
+{% endif %}
+
# Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
# close_ike_on_child_failure = no
diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl
index c6b71f2a1..a622cbf74 100644
--- a/data/templates/ipsec/swanctl/peer.tmpl
+++ b/data/templates/ipsec/swanctl/peer.tmpl
@@ -5,6 +5,9 @@
peer_{{ name }} {
proposals = {{ ike | get_esp_ike_cipher | join(',') }}
version = {{ ike.key_exchange[4:] if ike is defined and ike.key_exchange is defined else "0" }}
+{% if peer_conf.virtual_address is defined and peer_conf.virtual_address is not none %}
+ vips = {{ peer_conf.virtual_address | join(', ') }}
+{% endif %}
local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '0.0.0.0/0' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}
remote_addrs = {{ peer if peer not in ['any', '0.0.0.0'] and peer[0:1] != '@' else '0.0.0.0/0' }}
{% if peer_conf.authentication is defined and peer_conf.authentication.mode is defined and peer_conf.authentication.mode == 'x509' %}
@@ -57,6 +60,12 @@
{% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is defined else esp_group[ peer_conf.default_esp_group ] %}
peer_{{ name }}_vti {
esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }}
+{% if vti_esp.life_bytes is defined and vti_esp.life_bytes is not none %}
+ life_bytes = {{ vti_esp.life_bytes }}
+{% endif %}
+{% if vti_esp.life_packets is defined and vti_esp.life_packets is not none %}
+ life_packets = {{ vti_esp.life_packets }}
+{% endif %}
life_time = {{ vti_esp.lifetime }}s
local_ts = 0.0.0.0/0,::/0
remote_ts = 0.0.0.0/0,::/0
@@ -74,11 +83,14 @@
start_action = start
{% elif peer_conf.connection_type == 'respond' %}
start_action = trap
+{% elif peer_conf.connection_type == 'none' %}
+ start_action = none
{% endif %}
{% if ike.dead_peer_detection is defined %}
-{% set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'start'} %}
+{% set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'restart'} %}
dpd_action = {{ dpd_translate[ike.dead_peer_detection.action] }}
{% endif %}
+ close_action = {{ {'none': 'none', 'hold': 'trap', 'restart': 'start'}[ike.close_action] }}
}
{% elif peer_conf.tunnel is defined %}
{% for tunnel_id, tunnel_conf in peer_conf.tunnel.items() if tunnel_conf.disable is not defined %}
@@ -91,6 +103,12 @@
{% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %}
peer_{{ name }}_tunnel_{{ tunnel_id }} {
esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }}
+{% if tunnel_esp.life_bytes is defined and tunnel_esp.life_bytes is not none %}
+ life_bytes = {{ tunnel_esp.life_bytes }}
+{% endif %}
+{% if tunnel_esp.life_packets is defined and tunnel_esp.life_packets is not none %}
+ life_packets = {{ tunnel_esp.life_packets }}
+{% endif %}
life_time = {{ tunnel_esp.lifetime }}s
{% if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %}
{% if tunnel_conf.local is defined and tunnel_conf.local.prefix is defined %}
@@ -116,11 +134,14 @@
start_action = start
{% elif peer_conf.connection_type == 'respond' %}
start_action = trap
+{% elif peer_conf.connection_type == 'none' %}
+ start_action = none
{% endif %}
{% if ike.dead_peer_detection is defined %}
-{% set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'start'} %}
+{% set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'restart'} %}
dpd_action = {{ dpd_translate[ike.dead_peer_detection.action] }}
{% endif %}
+ close_action = {{ {'none': 'none', 'hold': 'trap', 'restart': 'start'}[ike.close_action] }}
{% if peer_conf.vti is defined and peer_conf.vti.bind is defined %}
updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}"
{# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #}
diff --git a/data/templates/lldp/lldpd.tmpl b/data/templates/lldp/lldpd.tmpl
index 3db955b48..819e70c84 100644
--- a/data/templates/lldp/lldpd.tmpl
+++ b/data/templates/lldp/lldpd.tmpl
@@ -1,3 +1,2 @@
### Autogenerated by lldp.py ###
-DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}"
-
+DAEMON_ARGS="-M 4{% if snmp is defined and snmp.enable is defined %} -x{% endif %}{% if legacy_protocols is defined and legacy_protocols.cdp is defined %} -c{% endif %}{% if legacy_protocols is defined and legacy_protocols.edp is defined %} -e{% endif %}{% if legacy_protocols is defined and legacy_protocols.fdp is defined %} -f{% endif %}{% if legacy_protocols is defined and legacy_protocols.sonmp is defined %} -s{% endif %}"
diff --git a/data/templates/lldp/vyos.conf.tmpl b/data/templates/lldp/vyos.conf.tmpl
index 07bbaf604..14395a223 100644
--- a/data/templates/lldp/vyos.conf.tmpl
+++ b/data/templates/lldp/vyos.conf.tmpl
@@ -1,20 +1,25 @@
### Autogenerated by lldp.py ###
configure system platform VyOS
-configure system description "VyOS {{ options.description }}"
-{% if options.listen_on %}
-configure system interface pattern "{{ ( options.listen_on | select('equalto','all') | map('replace','all','*') | list + options.listen_on | select('equalto','!all') | map('replace','!all','!*') | list + options.listen_on | reject('equalto','all') | reject('equalto','!all') | list ) | unique | join(",") }}"
+configure system description "VyOS {{ version }}"
+{% if interface is defined and interface is not none %}
+{% set tmp = [] %}
+{% for iface, iface_options in interface.items() if not iface_options.disable %}
+{% if iface == 'all' %}
+{% set iface = '*' %}
+{% endif %}
+{% set _ = tmp.append(iface) %}
+{% if iface_options.location is defined and iface_options.location is not none %}
+{% if iface_options.location.elin is defined and iface_options.location.elin is not none %}
+configure ports {{ iface }} med location elin "{{ iface_options.location.elin }}"
+{% endif %}
+{% if iface_options.location is defined and iface_options.location.coordinate_based is defined and iface_options.location.coordinate_based is not none %}
+configure ports {{ iface }} med location coordinate latitude "{{ iface_options.location.coordinate_based.latitude }}" longitude "{{ iface_options.location.coordinate_based.longitude }}" altitude "{{ iface_options.location.coordinate_based.altitude }}m" datum "{{ iface_options.location.coordinate_based.datum }}"
+{% endif %}
+{% endif %}
+{% endfor %}
+configure system interface pattern "{{ tmp | join(",") }}"
{% endif %}
-{% if options.mgmt_addr %}
-configure system ip management pattern {{ options.mgmt_addr | join(",") }}
+{% if management_address is defined and management_address is not none %}
+configure system ip management pattern {{ management_address | join(",") }}
{% endif %}
-{% for loc in location %}
-{% if loc.elin %}
-configure ports {{ loc.name }} med location elin "{{ loc.elin }}"
-{% endif %}
-{% if loc.coordinate_based %}
-configure ports {{ loc.name }} med location coordinate {% if loc.coordinate_based.latitude %}latitude {{ loc.coordinate_based.latitude }}{% endif %} {% if loc.coordinate_based.longitude %}longitude {{ loc.coordinate_based.longitude }}{% endif %} {% if loc.coordinate_based.altitude %}altitude {{ loc.coordinate_based.altitude }} m{% endif %} {% if loc.coordinate_based.datum %}datum {{ loc.coordinate_based.datum }}{% endif %}
-{% endif %}
-
-
-{% endfor %}
diff --git a/data/templates/monitoring/override.conf.tmpl b/data/templates/monitoring/override.conf.tmpl
index 63f6d7391..f8f150791 100644
--- a/data/templates/monitoring/override.conf.tmpl
+++ b/data/templates/monitoring/override.conf.tmpl
@@ -3,5 +3,5 @@ After=vyos-router.service
ConditionPathExists=/run/telegraf/vyos-telegraf.conf
[Service]
Environment=INFLUX_TOKEN={{ authentication.token }}
-CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN
+CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
diff --git a/data/templates/monitoring/telegraf.tmpl b/data/templates/monitoring/telegraf.tmpl
index 62fa4df7a..d3145a500 100644
--- a/data/templates/monitoring/telegraf.tmpl
+++ b/data/templates/monitoring/telegraf.tmpl
@@ -17,7 +17,7 @@
[[outputs.influxdb_v2]]
urls = ["{{ url }}:{{ port }}"]
insecure_skip_verify = true
- token = "{{ authentication.token }}"
+ token = "$INFLUX_TOKEN"
organization = "{{ authentication.organization }}"
bucket = "{{ bucket }}"
[[inputs.cpu]]
@@ -41,11 +41,7 @@
files = ["ip_conntrack_count","ip_conntrack_max","nf_conntrack_count","nf_conntrack_max"]
dirs = ["/proc/sys/net/ipv4/netfilter","/proc/sys/net/netfilter"]
[[inputs.ethtool]]
-[[inputs.iptables]]
- use_sudo = false
- table = "filter"
- chains = {{ nft_chains }}
- use_lock = true
+ interface_include = {{ interfaces_ethernet }}
[[inputs.ntpq]]
dns_lookup = true
[[inputs.internal]]
@@ -56,8 +52,9 @@
syslog_standard = "RFC3164"
[[inputs.exec]]
commands = [
+ "{{ custom_scripts_dir }}/show_firewall_input_filter.py",
"{{ custom_scripts_dir }}/show_interfaces_input_filter.py",
- "cat /tmp/vyos_services_input_filter"
+ "{{ custom_scripts_dir }}/vyos_services_input_filter.py"
]
timeout = "10s"
data_format = "influx"
diff --git a/data/templates/nhrp/opennhrp.conf.tmpl b/data/templates/nhrp/opennhrp.conf.tmpl
index 948327198..e9e9f692a 100644
--- a/data/templates/nhrp/opennhrp.conf.tmpl
+++ b/data/templates/nhrp/opennhrp.conf.tmpl
@@ -33,7 +33,7 @@ interface {{ name }} #{{ type }} {{ profile_name }}
{% endfor %}
{% if tunnel_conf.shortcut_target is defined and tunnel_conf.shortcut_target is not none %}
{% for target, shortcut_conf in tunnel_conf.shortcut_target.items() %}
- shortcut-target {{ target }} {{ shortcut_conf.holding_time if shortcut_conf.holding_time is defined else '' }}
+ shortcut-target {{ target }}{{ ' holding-time ' + shortcut_conf.holding_time if shortcut_conf.holding_time is defined }}
{% endfor %}
{% endif %}
diff --git a/data/templates/ntp/ntpd.conf.tmpl b/data/templates/ntp/ntpd.conf.tmpl
index 38e68f24f..e7afcc16b 100644
--- a/data/templates/ntp/ntpd.conf.tmpl
+++ b/data/templates/ntp/ntpd.conf.tmpl
@@ -27,6 +27,7 @@ restrict -6 ::1
{% if allow_clients is defined and allow_clients.address is defined %}
# Allowed clients configuration
+restrict default ignore
{% for address in allow_clients.address %}
restrict {{ address|address_from_cidr }} mask {{ address|netmask_from_cidr }} nomodify notrap nopeer
{% endfor %}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index 7a0470d0e..fb7ad9e16 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -141,11 +141,13 @@ ping {{ keep_alive.interval }}
ping-restart {{ keep_alive.failure_count }}
{% if device_type == 'tap' %}
-{% for laddr, laddr_conf in local_address.items() if laddr | is_ipv4 %}
-{% if laddr_conf is defined and laddr_conf.subnet_mask is defined and laddr_conf.subnet_mask is not none %}
+{% if local_address is defined and local_address is not none %}
+{% for laddr, laddr_conf in local_address.items() if laddr | is_ipv4 %}
+{% if laddr_conf is defined and laddr_conf.subnet_mask is defined and laddr_conf.subnet_mask is not none %}
ifconfig {{ laddr }} {{ laddr_conf.subnet_mask }}
-{% endif %}
-{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
{% else %}
{% for laddr in local_address if laddr | is_ipv4 %}
{% for raddr in remote_address if raddr | is_ipv4 %}
diff --git a/src/etc/systemd/system/uacctd.service.d/override.conf b/data/templates/pmacct/override.conf.tmpl
index 38bcce515..216927666 100644
--- a/src/etc/systemd/system/uacctd.service.d/override.conf
+++ b/data/templates/pmacct/override.conf.tmpl
@@ -1,3 +1,4 @@
+{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
[Unit]
After=
After=vyos-router.service
@@ -7,8 +8,10 @@ ConditionPathExists=/run/pmacct/uacctd.conf
[Service]
EnvironmentFile=
ExecStart=
-ExecStart=/usr/sbin/uacctd -f /run/pmacct/uacctd.conf
+ExecStart={{vrf_command}}/usr/sbin/uacctd -f /run/pmacct/uacctd.conf
WorkingDirectory=
WorkingDirectory=/run/pmacct
PIDFile=
PIDFile=/run/pmacct/uacctd.pid
+Restart=always
+RestartSec=10
diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/pmacct/uacctd.conf.tmpl
index f81002dc1..b58f7c796 100644
--- a/data/templates/netflow/uacctd.conf.tmpl
+++ b/data/templates/pmacct/uacctd.conf.tmpl
@@ -19,19 +19,19 @@ imt_mem_pools_number: 169
{% endif %}
{% set plugin = [] %}
-{% if disable_imt is not defined %}
-{% set plugin = ['memory'] %}
-{% endif %}
{% if netflow is defined and netflow.server is defined and netflow.server is not none %}
{% for server in netflow.server %}
-{% set plugin = plugin.append('nfprobe[nf_' ~ server ~ ']') %}
+{% set _ = plugin.append('nfprobe[nf_' ~ server ~ ']') %}
{% endfor %}
{% endif %}
{% if sflow is defined and sflow.server is defined and sflow.server is not none %}
{% for server in sflow.server %}
-{% set plugin = plugin.append('sfprobe[sf_' ~ server ~ ']') %}
+{% set _ = plugin.append('sfprobe[sf_' ~ server ~ ']') %}
{% endfor %}
{% endif %}
+{% if disable_imt is not defined %}
+{% set _ = plugin.append('memory') %}
+{% endif %}
plugins: {{ plugin | join(',') }}
{% if netflow is defined and netflow.server is defined and netflow.server is not none %}
diff --git a/data/templates/ssh/sshd_config.tmpl b/data/templates/ssh/sshd_config.tmpl
index 2f2b78a66..670cf85a1 100644
--- a/data/templates/ssh/sshd_config.tmpl
+++ b/data/templates/ssh/sshd_config.tmpl
@@ -29,6 +29,7 @@ UsePAM yes
PermitRootLogin no
PidFile /run/sshd/sshd.pid
AddressFamily any
+DebianBanner no
#
# User configurable section
diff --git a/data/templates/syslog/logrotate.tmpl b/data/templates/syslog/logrotate.tmpl
index f758265e4..c1b951e8b 100644
--- a/data/templates/syslog/logrotate.tmpl
+++ b/data/templates/syslog/logrotate.tmpl
@@ -1,12 +1,11 @@
-{% for file in files %}
-{{files[file]['log-file']}} {
+{{ config_render['log-file'] }} {
missingok
notifempty
create
- rotate {{files[file]['max-files']}}
- size={{files[file]['max-size']//1024}}k
+ rotate {{ config_render['max-files'] }}
+ size={{ config_render['max-size'] // 1024 }}k
postrotate
invoke-rc.d rsyslog rotate > /dev/null
endscript
}
-{% endfor %}
+
diff --git a/data/templates/zone_policy/nftables.tmpl b/data/templates/zone_policy/nftables.tmpl
index fae6e8c4f..4a6bd2772 100644
--- a/data/templates/zone_policy/nftables.tmpl
+++ b/data/templates/zone_policy/nftables.tmpl
@@ -13,29 +13,32 @@ table ip filter {
chain VZONE_{{ zone_name }}_IN {
iifname lo counter return
{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% endfor %}
- counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }}
+ counter {{ zone_conf.default_action }}
}
chain VZONE_{{ zone_name }}_OUT {
oifname lo counter return
{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.name is defined %}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }}
+ oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}
oifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% endfor %}
- counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }}
+ counter {{ zone_conf.default_action }}
}
{% else %}
chain VZONE_{{ zone_name }} {
iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=False) }}
+{% if zone_conf.intra_zone_filtering is defined %}
+ iifname { {{ zone_conf.interface | join(",") }} } counter return
+{% endif %}
{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %}
{% if zone[from_zone].local_zone is not defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% endif %}
{% endfor %}
- counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }}
+ counter {{ zone_conf.default_action }}
}
{% endif %}
{% endfor %}
@@ -47,29 +50,32 @@ table ip6 filter {
chain VZONE6_{{ zone_name }}_IN {
iifname lo counter return
{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% endfor %}
- counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }}
+ counter {{ zone_conf.default_action }}
}
chain VZONE6_{{ zone_name }}_OUT {
oifname lo counter return
{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.ipv6_name is defined %}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }}
+ oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}
oifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% endfor %}
- counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }}
+ counter {{ zone_conf.default_action }}
}
{% else %}
chain VZONE6_{{ zone_name }} {
iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=True) }}
+{% if zone_conf.intra_zone_filtering is defined %}
+ iifname { {{ zone_conf.interface | join(",") }} } counter return
+{% endif %}
{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %}
{% if zone[from_zone].local_zone is not defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% endif %}
{% endfor %}
- counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }}
+ counter {{ zone_conf.default_action }}
}
{% endif %}
{% endfor %}
diff --git a/debian/control b/debian/control
index 8afdd3300..c53e4d3b8 100644
--- a/debian/control
+++ b/debian/control
@@ -170,7 +170,9 @@ Depends:
wide-dhcpv6-client,
wireguard-tools,
wireless-regdb,
- wpasupplicant (>= 0.6.7)
+ wpasupplicant (>= 0.6.7),
+ ndppd,
+ miniupnpd-nftables
Description: VyOS configuration scripts and data
VyOS configuration scripts, interface definitions, and everything
diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index 63dff43a5..493c896eb 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -1,4 +1,3 @@
-etc/cron.d
etc/cron.hourly
etc/dhcp
etc/ipsec.d
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 4b4c4c13e..1ca6687a3 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -83,3 +83,17 @@ fi
if [ -L /etc/init.d/README ]; then
rm -f /etc/init.d/README
fi
+
+# Remove unwanted daemon files from /etc
+# conntackd
+DELETE="/etc/logrotate.d/conntrackd.distrib /etc/init.d/conntrackd /etc/default/conntrackd
+ /etc/default/pmacctd /etc/pmacct"
+for file in $DELETE; do
+ if [ -f ${file} ]; then
+ rm -f ${file}
+ fi
+done
+
+# Remove logrotate items controlled via CLI and VyOS defaults
+sed -i '/^\/var\/log\/messages$/d' /etc/logrotate.d/rsyslog
+sed -i '/^\/var\/log\/auth.log$/d' /etc/logrotate.d/rsyslog
diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst
index 45440bf64..71750b3a1 100644
--- a/debian/vyos-1x.preinst
+++ b/debian/vyos-1x.preinst
@@ -1,4 +1,4 @@
dpkg-divert --package vyos-1x --add --rename /etc/securetty
dpkg-divert --package vyos-1x --add --rename /etc/security/capability.conf
dpkg-divert --package vyos-1x --add --rename /lib/systemd/system/lcdproc.service
-
+dpkg-divert --package vyos-1x --add --rename /etc/logrotate.d/conntrackd
diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in
index 30c7110b8..9cd2b0902 100644
--- a/interface-definitions/containers.xml.in
+++ b/interface-definitions/containers.xml.in
@@ -58,6 +58,31 @@
</properties>
</leafNode>
#include <include/generic-description.xml.i>
+ <tagNode name="device">
+ <properties>
+ <help>Add a host device to the container</help>
+ </properties>
+ <children>
+ <leafNode name="source">
+ <properties>
+ <help>Source device (Example: "/dev/x")</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Source device</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="destination">
+ <properties>
+ <help>Destination container device (Example: "/dev/x")</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Destination container device</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
#include <include/generic-disable-node.xml.i>
<tagNode name="environment">
<properties>
@@ -86,7 +111,7 @@
</leafNode>
<leafNode name="memory">
<properties>
- <help>Constrain the memory available to a container (default: 512MB)</help>
+ <help>Constrain the memory available to a container</help>
<valueHelp>
<format>u32:0</format>
<description>Unlimited</description>
@@ -187,7 +212,7 @@
</valueHelp>
<valueHelp>
<format>on-failure</format>
- <description>Restart containers when they exit with a non-zero exit code, retrying indefinitely (default)</description>
+ <description>Restart containers when they exit with a non-zero exit code, retrying indefinitely</description>
</valueHelp>
<valueHelp>
<format>always</format>
@@ -258,7 +283,7 @@
</tagNode>
<leafNode name="registry">
<properties>
- <help>Add registry (default docker.io)</help>
+ <help>Add registry</help>
<multi/>
</properties>
<defaultValue>docker.io</defaultValue>
diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in
index 483e776a7..339941e65 100644
--- a/interface-definitions/dhcp-relay.xml.in
+++ b/interface-definitions/dhcp-relay.xml.in
@@ -20,7 +20,7 @@
<help>Policy to discard packets that have reached specified hop-count</help>
<valueHelp>
<format>u32:1-255</format>
- <description>Hop count (default: 10)</description>
+ <description>Hop count</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
@@ -34,17 +34,18 @@
<help>Maximum packet size to send to a DHCPv4/BOOTP server</help>
<valueHelp>
<format>u32:64-1400</format>
- <description>Maximum packet size (default: 576)</description>
+ <description>Maximum packet size</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 64-1400"/>
</constraint>
<constraintErrorMessage>max-size must be a value between 64 and 1400</constraintErrorMessage>
</properties>
+ <defaultValue>576</defaultValue>
</leafNode>
<leafNode name="relay-agents-packets">
<properties>
- <help>Policy to handle incoming DHCPv4 packets which already contain relay agent options (default: forward)</help>
+ <help>Policy to handle incoming DHCPv4 packets which already contain relay agent options</help>
<completionHelp>
<list>append replace forward discard</list>
</completionHelp>
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index b674e299e..4ea2d471d 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -203,7 +203,7 @@
</leafNode>
<leafNode name="lease">
<properties>
- <help>Lease timeout in seconds (default: 86400)</help>
+ <help>Lease timeout in seconds</help>
<valueHelp>
<format>u32</format>
<description>DHCP lease time in seconds</description>
diff --git a/interface-definitions/dhcpv6-relay.xml.in b/interface-definitions/dhcpv6-relay.xml.in
index 7162cf353..5abcbe804 100644
--- a/interface-definitions/dhcpv6-relay.xml.in
+++ b/interface-definitions/dhcpv6-relay.xml.in
@@ -36,7 +36,7 @@
<help>Maximum hop count for which requests will be processed</help>
<valueHelp>
<format>u32:1-255</format>
- <description>Hop count (default: 10)</description>
+ <description>Hop count</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in
index 005a55ab3..7ae537d00 100644
--- a/interface-definitions/dns-domain-name.xml.in
+++ b/interface-definitions/dns-domain-name.xml.in
@@ -29,6 +29,7 @@
</constraint>
</properties>
</leafNode>
+ <!-- script does not use XML defaults so far -->
<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-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 4faf604ad..08501a4b5 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -16,7 +16,7 @@
<children>
<leafNode name="cache-size">
<properties>
- <help>DNS forwarding cache size (default: 10000)</help>
+ <help>DNS forwarding cache size</help>
<valueHelp>
<format>u32:0-2147483647</format>
<description>DNS forwarding cache size</description>
@@ -38,7 +38,7 @@
</leafNode>
<leafNode name="dnssec">
<properties>
- <help>DNSSEC mode (default: process-no-validate)</help>
+ <help>DNSSEC mode</help>
<completionHelp>
<list>off process-no-validate process log-fail validate</list>
</completionHelp>
@@ -587,7 +587,7 @@
#include <include/listen-address.xml.i>
<leafNode name="negative-ttl">
<properties>
- <help>Maximum amount of time negative entries are cached (default: 3600)</help>
+ <help>Maximum amount of time negative entries are cached</help>
<valueHelp>
<format>u32:0-7200</format>
<description>Seconds to cache NXDOMAIN entries</description>
@@ -598,6 +598,19 @@
</properties>
<defaultValue>3600</defaultValue>
</leafNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Number of milliseconds to wait for a remote authoritative server to respond</help>
+ <valueHelp>
+ <format>u32:10-60000</format>
+ <description>Network timeout in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-60000"/>
+ </constraint>
+ </properties>
+ <defaultValue>1500</defaultValue>
+ </leafNode>
#include <include/name-server-ipv4-ipv6.xml.i>
<leafNode name="source-address">
<properties>
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 78a48a522..f2aca4b3a 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -74,6 +74,9 @@
<tagNode name="address-group">
<properties>
<help>Firewall address-group</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
<leafNode name="address">
@@ -100,6 +103,9 @@
<tagNode name="ipv6-address-group">
<properties>
<help>Firewall ipv6-address-group</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
<leafNode name="address">
@@ -109,8 +115,13 @@
<format>ipv6</format>
<description>IPv6 address to match</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6range</format>
+ <description>IPv6 range to match (e.g. 2002::1-2002::ff)</description>
+ </valueHelp>
<constraint>
<validator name="ipv6-address"/>
+ <validator name="ipv6-range"/>
</constraint>
<multi/>
</properties>
@@ -120,7 +131,10 @@
</tagNode>
<tagNode name="ipv6-network-group">
<properties>
- <help>Network-group member</help>
+ <help>Firewall ipv6-network-group</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -139,9 +153,36 @@
</leafNode>
</children>
</tagNode>
+ <tagNode name="mac-group">
+ <properties>
+ <help>Firewall mac-group</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <leafNode name="mac-address">
+ <properties>
+ <help>Mac-group member</help>
+ <valueHelp>
+ <format>&lt;MAC address&gt;</format>
+ <description>MAC address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
<tagNode name="network-group">
<properties>
<help>Firewall network-group</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -163,6 +204,9 @@
<tagNode name="port-group">
<properties>
<help>Firewall port-group</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -182,6 +226,9 @@
<description>Numbered port range (e.g. 1001-1050)</description>
</valueHelp>
<multi/>
+ <constraint>
+ <validator name="port-range"/>
+ </constraint>
</properties>
</leafNode>
</children>
@@ -211,6 +258,9 @@
<tagNode name="ipv6-name">
<properties>
<help>IPv6 firewall rule-set name</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
#include <include/firewall/name-default-action.xml.i>
@@ -300,182 +350,31 @@
<help>ICMPv6 type and code information</help>
</properties>
<children>
- <leafNode name="type">
+ <leafNode name="code">
<properties>
- <help>ICMP type-name</help>
- <completionHelp>
- <list>any echo-reply pong destination-unreachable network-unreachable host-unreachable protocol-unreachable port-unreachable fragmentation-needed source-route-failed network-unknown host-unknown network-prohibited host-prohibited TOS-network-unreachable TOS-host-unreachable communication-prohibited host-precedence-violation precedence-cutoff source-quench redirect network-redirect host-redirect TOS-network-redirect TOS host-redirect echo-request ping router-advertisement router-solicitation time-exceeded ttl-exceeded ttl-zero-during-transit ttl-zero-during-reassembly parameter-problem ip-header-bad required-option-missing timestamp-request timestamp-reply address-mask-request address-mask-reply packet-too-big</list>
- </completionHelp>
- <valueHelp>
- <format>any</format>
- <description>Any ICMP type/code</description>
- </valueHelp>
- <valueHelp>
- <format>echo-reply</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>pong</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>destination-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>protocol-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>port-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>fragmentation-needed</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>source-route-failed</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-unknown</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-unknown</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-prohibited</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-prohibited</format>
- <description>ICMP type/code name</description>
- </valueHelp>
+ <help>ICMPv6 code (0-255)</help>
<valueHelp>
- <format>TOS-network-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS-host-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>communication-prohibited</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-precedence-violation</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>precedence-cutoff</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>source-quench</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS-network-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS host-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>echo-request</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ping</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>router-advertisement</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>router-solicitation</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>time-exceeded</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ttl-exceeded</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ttl-zero-during-transit</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ttl-zero-during-reassembly</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>parameter-problem</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ip-header-bad</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>required-option-missing</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>timestamp-request</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>timestamp-reply</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>address-mask-request</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>address-mask-reply</format>
- <description>ICMP type/code name</description>
+ <format>u32:0-255</format>
+ <description>ICMPv6 code (0-255)</description>
</valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>ICMPv6 type (0-255)</help>
<valueHelp>
- <format>packet-too-big</format>
- <description>ICMP type/code name</description>
+ <format>u32:0-255</format>
+ <description>ICMPv6 type (0-255)</description>
</valueHelp>
<constraint>
- <regex>^(any|echo-reply|pong|destination-unreachable|network-unreachable|host-unreachable|protocol-unreachable|port-unreachable|fragmentation-needed|source-route-failed|network-unknown|host-unknown|network-prohibited|host-prohibited|TOS-network-unreachable|TOS-host-unreachable|communication-prohibited|host-precedence-violation|precedence-cutoff|source-quench|redirect|network-redirect|host-redirect|TOS-network-redirect|TOS host-redirect|echo-request|ping|router-advertisement|router-solicitation|time-exceeded|ttl-exceeded|ttl-zero-during-transit|ttl-zero-during-reassembly|parameter-problem|ip-header-bad|required-option-missing|timestamp-request|timestamp-reply|address-mask-request|address-mask-reply|packet-too-big)$</regex>
<validator name="numeric" argument="--range 0-255"/>
</constraint>
</properties>
</leafNode>
+ #include <include/firewall/icmpv6-type-name.xml.i>
</children>
</node>
</children>
@@ -545,6 +444,9 @@
<tagNode name="name">
<properties>
<help>IPv4 firewall rule-set name</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
#include <include/firewall/name-default-action.xml.i>
diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in
index 1b57d706c..133e45c72 100644
--- a/interface-definitions/flow-accounting-conf.xml.in
+++ b/interface-definitions/flow-accounting-conf.xml.in
@@ -14,7 +14,7 @@
<help>Buffer size</help>
<valueHelp>
<format>u32</format>
- <description>Buffer size in MiB (default: 10)</description>
+ <description>Buffer size in MiB</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4294967295"/>
@@ -27,7 +27,7 @@
<help>Specifies the maximum number of bytes to capture for each packet</help>
<valueHelp>
<format>u32:128-750</format>
- <description>Packet length in bytes (default: 128)</description>
+ <description>Packet length in bytes</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 128-750"/>
@@ -209,7 +209,7 @@
</valueHelp>
<valueHelp>
<format>9</format>
- <description>NetFlow version 9 (default)</description>
+ <description>NetFlow version 9</description>
</valueHelp>
<valueHelp>
<format>10</format>
@@ -240,7 +240,7 @@
<help>NetFlow port number</help>
<valueHelp>
<format>u32:1025-65535</format>
- <description>NetFlow port number (default: 2055)</description>
+ <description>NetFlow port number</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1025-65535"/>
@@ -260,7 +260,7 @@
<help>Expiry scan interval</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>Expiry scan interval (default: 60)</description>
+ <description>Expiry scan interval</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -273,7 +273,7 @@
<help>Generic flow timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>Generic flow timeout in seconds (default: 3600)</description>
+ <description>Generic flow timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -286,7 +286,7 @@
<help>ICMP timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>ICMP timeout in seconds (default: 300)</description>
+ <description>ICMP timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -299,7 +299,7 @@
<help>Max active timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>Max active timeout in seconds (default: 604800)</description>
+ <description>Max active timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -312,7 +312,7 @@
<help>TCP finish timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>TCP FIN timeout in seconds (default: 300)</description>
+ <description>TCP FIN timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -325,7 +325,7 @@
<help>TCP generic timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>TCP generic timeout in seconds (default: 3600)</description>
+ <description>TCP generic timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -338,7 +338,7 @@
<help>TCP reset timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>TCP RST timeout in seconds (default: 120)</description>
+ <description>TCP RST timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -351,7 +351,7 @@
<help>UDP timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>UDP timeout in seconds (default: 300)</description>
+ <description>UDP timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-2147483647"/>
@@ -418,7 +418,7 @@
<help>sFlow port number</help>
<valueHelp>
<format>u32:1025-65535</format>
- <description>sFlow port number (default: 6343)</description>
+ <description>sFlow port number</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1025-65535"/>
@@ -431,6 +431,7 @@
#include <include/source-address-ipv4-ipv6.xml.i>
</children>
</node>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index f46343c76..662052e12 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -22,7 +22,7 @@
<help>Advertise interval</help>
<valueHelp>
<format>u32:1-255</format>
- <description>Advertise interval in seconds (default: 1)</description>
+ <description>Advertise interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
@@ -79,7 +79,7 @@
<children>
<leafNode name="failure-count">
<properties>
- <help>Health check failure count required for transition to fault (default: 3)</help>
+ <help>Health check failure count required for transition to fault</help>
<constraint>
<validator name="numeric" argument="--positive" />
</constraint>
@@ -88,7 +88,7 @@
</leafNode>
<leafNode name="interval">
<properties>
- <help>Health check execution interval in seconds (default: 60)</help>
+ <help>Health check execution interval in seconds</help>
<constraint>
<validator name="numeric" argument="--positive"/>
</constraint>
@@ -160,7 +160,7 @@
</leafNode>
<leafNode name="priority">
<properties>
- <help>Router priority (default: 100)</help>
+ <help>Router priority</help>
<valueHelp>
<format>u32:1-255</format>
<description>Router priority</description>
@@ -177,8 +177,37 @@
<valueless/>
</properties>
</leafNode>
+ <node name="track">
+ <properties>
+ <help>Track settings</help>
+ </properties>
+ <children>
+ <leafNode name="exclude-vrrp-interface">
+ <properties>
+ <valueless/>
+ <help>Disable track state of main interface</help>
+ </properties>
+ </leafNode>
+ <leafNode name="interface">
+ <properties>
+ <help>Interface name state check</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="interface-name"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
#include <include/vrrp-transition-script.xml.i>
- <leafNode name="address">
+ <tagNode name="address">
<properties>
<help>Virtual IP address</help>
<valueHelp>
@@ -193,9 +222,11 @@
<validator name="ipv4-host"/>
<validator name="ipv6-host"/>
</constraint>
- <multi/>
</properties>
- </leafNode>
+ <children>
+ #include <include/generic-interface-broadcast.xml.i>
+ </children>
+ </tagNode>
<leafNode name="excluded-address">
<properties>
<help>Virtual address (If you need additional IPv4 and IPv6 in same group)</help>
@@ -302,7 +333,7 @@
<help>Interval between health-checks (in seconds)</help>
<valueHelp>
<format>u32:1-600</format>
- <description>Interval in seconds (default: 10)</description>
+ <description>Interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-3600"/>
@@ -312,7 +343,7 @@
</leafNode>
<leafNode name="forward-method">
<properties>
- <help>Forwarding method (default: NAT)</help>
+ <help>Forwarding method</help>
<completionHelp>
<list>direct nat tunnel</list>
</completionHelp>
@@ -340,7 +371,7 @@
<help>Timeout for persistent connections</help>
<valueHelp>
<format>u32:1-86400</format>
- <description>Timeout for persistent connections (default: 300)</description>
+ <description>Timeout for persistent connections</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-86400"/>
@@ -350,7 +381,7 @@
</leafNode>
<leafNode name="protocol">
<properties>
- <help>Protocol for port checks (default: TCP)</help>
+ <help>Protocol for port checks</help>
<completionHelp>
<list>tcp udp</list>
</completionHelp>
diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in
index 91c912d8b..c7ab60929 100644
--- a/interface-definitions/igmp-proxy.xml.in
+++ b/interface-definitions/igmp-proxy.xml.in
@@ -39,7 +39,7 @@
</leafNode>
<leafNode name="role">
<properties>
- <help>IGMP interface role (default: downstream)</help>
+ <help>IGMP interface role</help>
<completionHelp>
<list>upstream downstream disabled</list>
</completionHelp>
@@ -49,7 +49,7 @@
</valueHelp>
<valueHelp>
<format>downstream</format>
- <description>Downstream interface(s) (default)</description>
+ <description>Downstream interface(s)</description>
</valueHelp>
<valueHelp>
<format>disabled</format>
@@ -63,10 +63,10 @@
</leafNode>
<leafNode name="threshold">
<properties>
- <help>TTL threshold (default: 1)</help>
+ <help>TTL threshold</help>
<valueHelp>
<format>u32:1-255</format>
- <description>TTL threshold for the interfaces (default: 1)</description>
+ <description>TTL threshold for the interfaces</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
diff --git a/interface-definitions/include/accel-ppp/client-ip-pool-subnet-single.xml.i b/interface-definitions/include/accel-ppp/client-ip-pool-subnet-single.xml.i
new file mode 100644
index 000000000..e5918b765
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/client-ip-pool-subnet-single.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from accel-ppp/client-ip-pool-subnet-single.xml.i -->
+<leafNode name="subnet">
+ <properties>
+ <help>Client IP subnet (CIDR notation)</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ <constraintErrorMessage>Not a valid CIDR formatted prefix</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
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 a692f2335..01cf0e040 100644
--- a/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
+++ b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
@@ -21,7 +21,7 @@
<help>Prefix length used for individual client</help>
<valueHelp>
<format>u32:48-128</format>
- <description>Client prefix length (default: 64)</description>
+ <description>Client prefix length</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 48-128"/>
diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i
index 258ece2b5..441c9dda5 100644
--- a/interface-definitions/include/accel-ppp/radius-additions.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i
@@ -21,7 +21,7 @@
<help>Accounting port</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>Numeric IP port (default: 1813)</description>
+ <description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
@@ -62,7 +62,7 @@
</leafNode>
<leafNode name="acct-timeout">
<properties>
- <help>Timeout for Interim-Update packets, terminate session afterwards (default 3 seconds)</help>
+ <help>Timeout for Interim-Update packets, terminate session afterwards</help>
<valueHelp>
<format>u32:0-60</format>
<description>Timeout in seconds, 0 to keep active</description>
@@ -126,7 +126,7 @@
</leafNode>
<leafNode name="port">
<properties>
- <help>Port for Dynamic Authorization Extension server (DM/CoA) (default: 1700)</help>
+ <help>Port for Dynamic Authorization Extension server (DM/CoA)</help>
<valueHelp>
<format>u32:1-65535</format>
<description>TCP port</description>
diff --git a/interface-definitions/include/arp-ndp-table-size.xml.i b/interface-definitions/include/arp-ndp-table-size.xml.i
new file mode 100644
index 000000000..dec86e91a
--- /dev/null
+++ b/interface-definitions/include/arp-ndp-table-size.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from arp-ndp-table-size.xml.i -->
+<leafNode name="table-size">
+ <properties>
+ <help>Maximum number of entries to keep in the cache</help>
+ <completionHelp>
+ <list>1024 2048 4096 8192 16384 32768</list>
+ </completionHelp>
+ <constraint>
+ <regex>(1024|2048|4096|8192|16384|32768)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>8192</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bfd/common.xml.i b/interface-definitions/include/bfd/common.xml.i
index e52221441..126ab9b9a 100644
--- a/interface-definitions/include/bfd/common.xml.i
+++ b/interface-definitions/include/bfd/common.xml.i
@@ -15,7 +15,7 @@
<help>Minimum interval of receiving control packets</help>
<valueHelp>
<format>u32:10-60000</format>
- <description>Interval in milliseconds (default: 300)</description>
+ <description>Interval in milliseconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 10-60000"/>
@@ -28,7 +28,7 @@
<help>Minimum interval of transmitting control packets</help>
<valueHelp>
<format>u32:10-60000</format>
- <description>Interval in milliseconds (default: 300)</description>
+ <description>Interval in milliseconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 10-60000"/>
@@ -41,7 +41,7 @@
<help>Multiplier to determine packet loss</help>
<valueHelp>
<format>u32:2-255</format>
- <description>Remote transmission interval will be multiplied by this value (default: 3)</description>
+ <description>Remote transmission interval will be multiplied by this value</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 2-255"/>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 8214d0779..b59ff0287 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1191,7 +1191,7 @@
<help>Set period to rescan BGP table to check if condition is met</help>
<valueHelp>
<format>u32:5-240</format>
- <description>Period to rerun the conditional advertisement scanner process (default: 60)</description>
+ <description>Period to rerun the conditional advertisement scanner process</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 5-240"/>
@@ -1430,6 +1430,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="no-suppress-duplicates">
+ <properties>
+ <help>Disable suppress duplicate updates if the route actually not changed</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="reject-as-sets">
<properties>
<help>Reject routes with AS_SET or AS_CONFED_SET flag</help>
diff --git a/interface-definitions/include/bgp/timers-keepalive.xml.i b/interface-definitions/include/bgp/timers-keepalive.xml.i
index b2771e326..b23f96ec8 100644
--- a/interface-definitions/include/bgp/timers-keepalive.xml.i
+++ b/interface-definitions/include/bgp/timers-keepalive.xml.i
@@ -4,7 +4,7 @@
<help>BGP keepalive interval for this neighbor</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>Keepalive interval in seconds (default 60)</description>
+ <description>Keepalive interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
diff --git a/interface-definitions/include/conntrack/log-common.xml.i b/interface-definitions/include/conntrack/log-common.xml.i
new file mode 100644
index 000000000..38799f8f4
--- /dev/null
+++ b/interface-definitions/include/conntrack/log-common.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from conntrack/log-common.xml.i -->
+<leafNode name="destroy">
+ <properties>
+ <help>Log connection deletion</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="new">
+ <properties>
+ <help>Log connection creation</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="update">
+ <properties>
+ <help>Log connection updates</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/conntrack/timeout-common-protocols.xml.i b/interface-definitions/include/conntrack/timeout-common-protocols.xml.i
new file mode 100644
index 000000000..2676d846e
--- /dev/null
+++ b/interface-definitions/include/conntrack/timeout-common-protocols.xml.i
@@ -0,0 +1,172 @@
+<!-- include start from conntrack/timeout-common-protocols.xml.i -->
+<leafNode name="icmp">
+ <properties>
+ <help>ICMP timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>ICMP timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>30</defaultValue>
+</leafNode>
+<leafNode name="other">
+ <properties>
+ <help>Generic connection timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>Generic connection timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>600</defaultValue>
+</leafNode>
+<node name="tcp">
+ <properties>
+ <help>TCP connection timeout options</help>
+ </properties>
+ <children>
+ <leafNode name="close-wait">
+ <properties>
+ <help>TCP CLOSE-WAIT timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP CLOSE-WAIT timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>60</defaultValue>
+ </leafNode>
+ <leafNode name="close">
+ <properties>
+ <help>TCP CLOSE timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP CLOSE timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ <leafNode name="established">
+ <properties>
+ <help>TCP ESTABLISHED timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP ESTABLISHED timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>432000</defaultValue>
+ </leafNode>
+ <leafNode name="fin-wait">
+ <properties>
+ <help>TCP FIN-WAIT timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP FIN-WAIT timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>120</defaultValue>
+ </leafNode>
+ <leafNode name="last-ack">
+ <properties>
+ <help>TCP LAST-ACK timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP LAST-ACK timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>30</defaultValue>
+ </leafNode>
+ <leafNode name="syn-recv">
+ <properties>
+ <help>TCP SYN-RECEIVED timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP SYN-RECEIVED timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>60</defaultValue>
+ </leafNode>
+ <leafNode name="syn-sent">
+ <properties>
+ <help>TCP SYN-SENT timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP SYN-SENT timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>120</defaultValue>
+ </leafNode>
+ <leafNode name="time-wait">
+ <properties>
+ <help>TCP TIME-WAIT timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>TCP TIME-WAIT timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>120</defaultValue>
+ </leafNode>
+ </children>
+</node>
+<node name="udp">
+ <properties>
+ <help>UDP timeout options</help>
+ </properties>
+ <children>
+ <leafNode name="other">
+ <properties>
+ <help>UDP generic timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>UDP generic timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>30</defaultValue>
+ </leafNode>
+ <leafNode name="stream">
+ <properties>
+ <help>UDP stream timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-21474836</format>
+ <description>UDP stream timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-21474836"/>
+ </constraint>
+ </properties>
+ <defaultValue>180</defaultValue>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i
index 92950cc68..cd80b7e28 100644
--- a/interface-definitions/include/firewall/common-rule.xml.i
+++ b/interface-definitions/include/firewall/common-rule.xml.i
@@ -66,11 +66,11 @@
<properties>
<help>Maximum average matching rate</help>
<valueHelp>
- <format>u32:0-4294967295</format>
- <description>Maximum average matching rate</description>
+ <format>txt</format>
+ <description>integer/unit (Example: 5/minute)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-4294967295"/>
+ <regex>^\d+/(second|minute|hour|day)$</regex>
</constraint>
</properties>
</leafNode>
@@ -146,13 +146,24 @@
</leafNode>
<leafNode name="time">
<properties>
- <help>Source addresses seen in the last N seconds</help>
+ <help>Source addresses seen in the last second/minute/hour</help>
+ <completionHelp>
+ <list>second minute hour</list>
+ </completionHelp>
<valueHelp>
- <format>u32:0-4294967295</format>
- <description>Source addresses seen in the last N seconds</description>
+ <format>second</format>
+ <description>Source addresses seen COUNT times in the last second</description>
+ </valueHelp>
+ <valueHelp>
+ <format>minute</format>
+ <description>Source addresses seen COUNT times in the last minute</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hour</format>
+ <description>Source addresses seen COUNT times in the last hour</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-4294967295"/>
+ <regex>^(second|minute|hour)$</regex>
</constraint>
</properties>
</leafNode>
@@ -176,6 +187,9 @@
<format>!&lt;MAC address&gt;</format>
<description>Match everything except the specified MAC address</description>
</valueHelp>
+ <constraint>
+ <validator name="mac-address-firewall"/>
+ </constraint>
</properties>
</leafNode>
#include <include/firewall/port.xml.i>
@@ -264,26 +278,7 @@
</leafNode>
</children>
</node>
-<node name="tcp">
- <properties>
- <help>TCP flags to match</help>
- </properties>
- <children>
- <leafNode name="flags">
- <properties>
- <help>TCP flags to match</help>
- <valueHelp>
- <format>txt</format>
- <description>TCP flags to match</description>
- </valueHelp>
- <valueHelp>
- <format> </format>
- <description>\n\n Allowed values for TCP flags : SYN ACK FIN RST URG PSH ALL\n When specifying more than one flag, flags should be comma-separated.\n For example : value of 'SYN,!ACK,!FIN,!RST' will only match packets with\n the SYN flag set, and the ACK, FIN and RST flags unset</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
-</node>
+#include <include/firewall/tcp-flags.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
diff --git a/interface-definitions/include/firewall/icmp-type-name.xml.i b/interface-definitions/include/firewall/icmp-type-name.xml.i
index b45fb619b..f57def3e1 100644
--- a/interface-definitions/include/firewall/icmp-type-name.xml.i
+++ b/interface-definitions/include/firewall/icmp-type-name.xml.i
@@ -3,170 +3,70 @@
<properties>
<help>ICMP type-name</help>
<completionHelp>
- <list>any echo-reply pong destination-unreachable network-unreachable host-unreachable protocol-unreachable port-unreachable fragmentation-needed source-route-failed network-unknown host-unknown network-prohibited host-prohibited TOS-network-unreachable TOS-host-unreachable communication-prohibited host-precedence-violation precedence-cutoff source-quench redirect network-redirect host-redirect TOS-network-redirect TOS host-redirect echo-request ping router-advertisement router-solicitation time-exceeded ttl-exceeded ttl-zero-during-transit ttl-zero-during-reassembly parameter-problem ip-header-bad required-option-missing timestamp-request timestamp-reply address-mask-request address-mask-reply</list>
+ <list>echo-reply destination-unreachable source-quench redirect echo-request router-advertisement router-solicitation time-exceeded parameter-problem timestamp-request timestamp-reply info-request info-reply address-mask-request address-mask-reply</list>
</completionHelp>
<valueHelp>
- <format>any</format>
- <description>Any ICMP type/code</description>
- </valueHelp>
- <valueHelp>
<format>echo-reply</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>pong</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 0: echo-reply</description>
</valueHelp>
<valueHelp>
<format>destination-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>protocol-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>port-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>fragmentation-needed</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>source-route-failed</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-unknown</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-unknown</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-prohibited</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-prohibited</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS-network-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS-host-unreachable</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>communication-prohibited</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-precedence-violation</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>precedence-cutoff</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 3: destination-unreachable</description>
</valueHelp>
<valueHelp>
<format>source-quench</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 4: source-quench</description>
</valueHelp>
<valueHelp>
<format>redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>network-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>host-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS-network-redirect</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>TOS host-redirect</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 5: redirect</description>
</valueHelp>
<valueHelp>
<format>echo-request</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ping</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 8: echo-request</description>
</valueHelp>
<valueHelp>
<format>router-advertisement</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 9: router-advertisement</description>
</valueHelp>
<valueHelp>
<format>router-solicitation</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 10: router-solicitation</description>
</valueHelp>
<valueHelp>
<format>time-exceeded</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ttl-exceeded</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ttl-zero-during-transit</format>
- <description>ICMP type/code name</description>
- </valueHelp>
- <valueHelp>
- <format>ttl-zero-during-reassembly</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 11: time-exceeded</description>
</valueHelp>
<valueHelp>
<format>parameter-problem</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 12: parameter-problem</description>
</valueHelp>
<valueHelp>
- <format>ip-header-bad</format>
- <description>ICMP type/code name</description>
+ <format>timestamp-request</format>
+ <description>ICMP type 13: timestamp-request</description>
</valueHelp>
<valueHelp>
- <format>required-option-missing</format>
- <description>ICMP type/code name</description>
+ <format>timestamp-reply</format>
+ <description>ICMP type 14: timestamp-reply</description>
</valueHelp>
<valueHelp>
- <format>timestamp-request</format>
- <description>ICMP type/code name</description>
+ <format>info-request</format>
+ <description>ICMP type 15: info-request</description>
</valueHelp>
<valueHelp>
- <format>timestamp-reply</format>
- <description>ICMP type/code name</description>
+ <format>info-reply</format>
+ <description>ICMP type 16: info-reply</description>
</valueHelp>
<valueHelp>
<format>address-mask-request</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 17: address-mask-request</description>
</valueHelp>
<valueHelp>
<format>address-mask-reply</format>
- <description>ICMP type/code name</description>
+ <description>ICMP type 18: address-mask-reply</description>
</valueHelp>
<constraint>
- <regex>^(any|echo-reply|pong|destination-unreachable|network-unreachable|host-unreachable|protocol-unreachable|port-unreachable|fragmentation-needed|source-route-failed|network-unknown|host-unknown|network-prohibited|host-prohibited|TOS-network-unreachable|TOS-host-unreachable|communication-prohibited|host-precedence-violation|precedence-cutoff|source-quench|redirect|network-redirect|host-redirect|TOS-network-redirect|TOS host-redirect|echo-request|ping|router-advertisement|router-solicitation|time-exceeded|ttl-exceeded|ttl-zero-during-transit|ttl-zero-during-reassembly|parameter-problem|ip-header-bad|required-option-missing|timestamp-request|timestamp-reply|address-mask-request|address-mask-reply)$</regex>
+ <regex>^(echo-reply|destination-unreachable|source-quench|redirect|echo-request|router-advertisement|router-solicitation|time-exceeded|parameter-problem|timestamp-request|timestamp-reply|info-request|info-reply|address-mask-request|address-mask-reply)$</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/icmpv6-type-name.xml.i b/interface-definitions/include/firewall/icmpv6-type-name.xml.i
new file mode 100644
index 000000000..b13cf02c4
--- /dev/null
+++ b/interface-definitions/include/firewall/icmpv6-type-name.xml.i
@@ -0,0 +1,73 @@
+<!-- include start from firewall/icmpv6-type-name.xml.i -->
+<leafNode name="type-name">
+ <properties>
+ <help>ICMPv6 type-name</help>
+ <completionHelp>
+ <list>destination-unreachable packet-too-big time-exceeded echo-request echo-reply mld-listener-query mld-listener-report mld-listener-reduction nd-router-solicit nd-router-advert nd-neighbor-solicit nd-neighbor-advert nd-redirect parameter-problem router-renumbering</list>
+ </completionHelp>
+ <valueHelp>
+ <format>destination-unreachable</format>
+ <description>ICMPv6 type 1: destination-unreachable</description>
+ </valueHelp>
+ <valueHelp>
+ <format>packet-too-big</format>
+ <description>ICMPv6 type 2: packet-too-big</description>
+ </valueHelp>
+ <valueHelp>
+ <format>time-exceeded</format>
+ <description>ICMPv6 type 3: time-exceeded</description>
+ </valueHelp>
+ <valueHelp>
+ <format>echo-request</format>
+ <description>ICMPv6 type 128: echo-request</description>
+ </valueHelp>
+ <valueHelp>
+ <format>echo-reply</format>
+ <description>ICMPv6 type 129: echo-reply</description>
+ </valueHelp>
+ <valueHelp>
+ <format>mld-listener-query</format>
+ <description>ICMPv6 type 130: mld-listener-query</description>
+ </valueHelp>
+ <valueHelp>
+ <format>mld-listener-report</format>
+ <description>ICMPv6 type 131: mld-listener-report</description>
+ </valueHelp>
+ <valueHelp>
+ <format>mld-listener-reduction</format>
+ <description>ICMPv6 type 132: mld-listener-reduction</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nd-router-solicit</format>
+ <description>ICMPv6 type 133: nd-router-solicit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nd-router-advert</format>
+ <description>ICMPv6 type 134: nd-router-advert</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nd-neighbor-solicit</format>
+ <description>ICMPv6 type 135: nd-neighbor-solicit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nd-neighbor-advert</format>
+ <description>ICMPv6 type 136: nd-neighbor-advert</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nd-redirect</format>
+ <description>ICMPv6 type 137: nd-redirect</description>
+ </valueHelp>
+ <valueHelp>
+ <format>parameter-problem</format>
+ <description>ICMPv6 type 4: parameter-problem</description>
+ </valueHelp>
+ <valueHelp>
+ <format>router-renumbering</format>
+ <description>ICMPv6 type 138: router-renumbering</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(destination-unreachable|packet-too-big|time-exceeded|echo-request|echo-reply|mld-listener-query|mld-listener-report|mld-listener-reduction|nd-router-solicit|nd-router-advert|nd-neighbor-solicit|nd-neighbor-advert|nd-redirect|parameter-problem|router-renumbering)$</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/mac-group.xml.i b/interface-definitions/include/firewall/mac-group.xml.i
new file mode 100644
index 000000000..dbce3fc88
--- /dev/null
+++ b/interface-definitions/include/firewall/mac-group.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from firewall/mac-group.xml.i -->
+<leafNode name="mac-group">
+ <properties>
+ <help>Group of MAC addresses</help>
+ <completionHelp>
+ <path>firewall group mac-group</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<!-- include start from firewall/mac-group.xml.i --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/name-default-action.xml.i b/interface-definitions/include/firewall/name-default-action.xml.i
index 1b61b076f..8470a29a9 100644
--- a/interface-definitions/include/firewall/name-default-action.xml.i
+++ b/interface-definitions/include/firewall/name-default-action.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
<valueHelp>
<format>drop</format>
- <description>Drop if no prior rules are hit (default)</description>
+ <description>Drop if no prior rules are hit</description>
</valueHelp>
<valueHelp>
<format>reject</format>
diff --git a/interface-definitions/include/firewall/port.xml.i b/interface-definitions/include/firewall/port.xml.i
index 59d92978b..3bacafff8 100644
--- a/interface-definitions/include/firewall/port.xml.i
+++ b/interface-definitions/include/firewall/port.xml.i
@@ -16,8 +16,11 @@
</valueHelp>
<valueHelp>
<format> </format>
- <description>\n\n Multiple destination ports can be specified as a comma-separated list.\n The whole list can also be negated using '!'.\n For example: '!22,telnet,http,123,1001-1005'</description>
+ <description>\n\n Multiple destination ports can be specified as a comma-separated list.\n For example: 'telnet,http,123,1001-1005'</description>
</valueHelp>
+ <constraint>
+ <validator name="port-multi"/>
+ </constraint>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/firewall/source-destination-group-ipv6.xml.i b/interface-definitions/include/firewall/source-destination-group-ipv6.xml.i
index 7815b78d4..c2cc7edb3 100644
--- a/interface-definitions/include/firewall/source-destination-group-ipv6.xml.i
+++ b/interface-definitions/include/firewall/source-destination-group-ipv6.xml.i
@@ -12,6 +12,7 @@
</completionHelp>
</properties>
</leafNode>
+ #include <include/firewall/mac-group.xml.i>
<leafNode name="network-group">
<properties>
<help>Group of networks</help>
diff --git a/interface-definitions/include/firewall/source-destination-group.xml.i b/interface-definitions/include/firewall/source-destination-group.xml.i
index 9a9bed0fe..ab11e89e9 100644
--- a/interface-definitions/include/firewall/source-destination-group.xml.i
+++ b/interface-definitions/include/firewall/source-destination-group.xml.i
@@ -12,6 +12,7 @@
</completionHelp>
</properties>
</leafNode>
+ #include <include/firewall/mac-group.xml.i>
<leafNode name="network-group">
<properties>
<help>Group of networks</help>
diff --git a/interface-definitions/include/firewall/tcp-flags.xml.i b/interface-definitions/include/firewall/tcp-flags.xml.i
new file mode 100644
index 000000000..b99896687
--- /dev/null
+++ b/interface-definitions/include/firewall/tcp-flags.xml.i
@@ -0,0 +1,119 @@
+<!-- include start from firewall/tcp-flags.xml.i -->
+<node name="tcp">
+ <properties>
+ <help>TCP flags to match</help>
+ </properties>
+ <children>
+ <node name="flags">
+ <properties>
+ <help>TCP flags to match</help>
+ </properties>
+ <children>
+ <leafNode name="syn">
+ <properties>
+ <help>Synchronise flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ack">
+ <properties>
+ <help>Acknowledge flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="fin">
+ <properties>
+ <help>Finish flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="rst">
+ <properties>
+ <help>Reset flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="urg">
+ <properties>
+ <help>Urgent flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="psh">
+ <properties>
+ <help>Push flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ecn">
+ <properties>
+ <help>Explicit Congestion Notification flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="cwr">
+ <properties>
+ <help>Congestion Window Reduced flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="not">
+ <properties>
+ <help>Match flags not set</help>
+ </properties>
+ <children>
+ <leafNode name="syn">
+ <properties>
+ <help>Synchronise flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ack">
+ <properties>
+ <help>Acknowledge flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="fin">
+ <properties>
+ <help>Finish flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="rst">
+ <properties>
+ <help>Reset flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="urg">
+ <properties>
+ <help>Urgent flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="psh">
+ <properties>
+ <help>Push flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ecn">
+ <properties>
+ <help>Explicit Congestion Notification flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="cwr">
+ <properties>
+ <help>Congestion Window Reduced flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/arp-cache-timeout.xml.i b/interface-definitions/include/interface/arp-cache-timeout.xml.i
index cb01d0525..06d7ffe96 100644
--- a/interface-definitions/include/interface/arp-cache-timeout.xml.i
+++ b/interface-definitions/include/interface/arp-cache-timeout.xml.i
@@ -4,7 +4,7 @@
<help>ARP cache entry timeout in seconds</help>
<valueHelp>
<format>u32:1-86400</format>
- <description>ARP cache entry timout in seconds (default 30)</description>
+ <description>ARP cache entry timout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-86400"/>
diff --git a/interface-definitions/include/interface/dhcp-options.xml.i b/interface-definitions/include/interface/dhcp-options.xml.i
index b65b0802a..098d02919 100644
--- a/interface-definitions/include/interface/dhcp-options.xml.i
+++ b/interface-definitions/include/interface/dhcp-options.xml.i
@@ -30,12 +30,13 @@
<help>Distance for the default route from DHCP server</help>
<valueHelp>
<format>u32:1-255</format>
- <description>Distance for the default route from DHCP server (default 210)</description>
+ <description>Distance for the default route from DHCP server</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
</constraint>
</properties>
+ <defaultValue>210</defaultValue>
</leafNode>
<leafNode name="reject">
<properties>
diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i
index d1abf4a90..08e4f5e0a 100644
--- a/interface-definitions/include/interface/dhcpv6-options.xml.i
+++ b/interface-definitions/include/interface/dhcpv6-options.xml.i
@@ -57,10 +57,10 @@
<children>
<leafNode name="address">
<properties>
- <help>Local interface address assigned to interface</help>
+ <help>Local interface address assigned to interface (default: EUI-64)</help>
<valueHelp>
<format>&gt;0</format>
- <description>Used to form IPv6 interface address (default: EUI-64)</description>
+ <description>Used to form IPv6 interface address</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--non-negative"/>
diff --git a/interface-definitions/include/interface/inbound-interface.xml.i b/interface-definitions/include/interface/inbound-interface.xml.i
new file mode 100644
index 000000000..5a8d47280
--- /dev/null
+++ b/interface-definitions/include/interface/inbound-interface.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from interface/inbound-interface.xml.i -->
+<leafNode name="inbound-interface">
+ <properties>
+ <help>Inbound Interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/interface-policy-vif-c.xml.i b/interface-definitions/include/interface/interface-policy-vif-c.xml.i
index 5dad6422b..866fcd5c0 100644
--- a/interface-definitions/include/interface/interface-policy-vif-c.xml.i
+++ b/interface-definitions/include/interface/interface-policy-vif-c.xml.i
@@ -13,11 +13,11 @@
</completionHelp>
</properties>
</leafNode>
- <leafNode name="ipv6-route">
+ <leafNode name="route6">
<properties>
<help>IPv6 policy route ruleset for interface</help>
<completionHelp>
- <path>policy ipv6-route</path>
+ <path>policy route6</path>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/interface/interface-policy-vif.xml.i b/interface-definitions/include/interface/interface-policy-vif.xml.i
index 5ee80ae13..83510fe59 100644
--- a/interface-definitions/include/interface/interface-policy-vif.xml.i
+++ b/interface-definitions/include/interface/interface-policy-vif.xml.i
@@ -13,11 +13,11 @@
</completionHelp>
</properties>
</leafNode>
- <leafNode name="ipv6-route">
+ <leafNode name="route6">
<properties>
<help>IPv6 policy route ruleset for interface</help>
<completionHelp>
- <path>policy ipv6-route</path>
+ <path>policy route6</path>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/interface/interface-policy.xml.i b/interface-definitions/include/interface/interface-policy.xml.i
index 06f025af1..42a8fd009 100644
--- a/interface-definitions/include/interface/interface-policy.xml.i
+++ b/interface-definitions/include/interface/interface-policy.xml.i
@@ -13,11 +13,11 @@
</completionHelp>
</properties>
</leafNode>
- <leafNode name="ipv6-route">
+ <leafNode name="route6">
<properties>
<help>IPv6 policy route ruleset for interface</help>
<completionHelp>
- <path>policy ipv6-route</path>
+ <path>policy route6</path>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/interface/redirect.xml.i b/interface-definitions/include/interface/redirect.xml.i
new file mode 100644
index 000000000..3be9ee16b
--- /dev/null
+++ b/interface-definitions/include/interface/redirect.xml.i
@@ -0,0 +1,17 @@
+<!-- include start from interface/redirect.xml.i -->
+<leafNode name="redirect">
+ <properties>
+ <help>Incoming packet redirection destination</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="interface-name"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/tunnel-remote-multi.xml.i b/interface-definitions/include/interface/tunnel-remote-multi.xml.i
new file mode 100644
index 000000000..f672087a4
--- /dev/null
+++ b/interface-definitions/include/interface/tunnel-remote-multi.xml.i
@@ -0,0 +1,19 @@
+<!-- include start from interface/tunnel-remote-multi.xml.i -->
+<leafNode name="remote">
+ <properties>
+ <help>Tunnel remote address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Tunnel remote IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Tunnel remote IPv6 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/tunnel-remote.xml.i b/interface-definitions/include/interface/tunnel-remote.xml.i
index 1ba9b0382..2a8891b85 100644
--- a/interface-definitions/include/interface/tunnel-remote.xml.i
+++ b/interface-definitions/include/interface/tunnel-remote.xml.i
@@ -1,4 +1,4 @@
-<!-- include start from rip/tunnel-remote.xml.i -->
+<!-- include start from interface/tunnel-remote.xml.i -->
<leafNode name="remote">
<properties>
<help>Tunnel remote address</help>
diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i
index f1a61ff64..3b305618e 100644
--- a/interface-definitions/include/interface/vif-s.xml.i
+++ b/interface-definitions/include/interface/vif-s.xml.i
@@ -44,6 +44,7 @@
#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>
#include <include/interface/mtu-68-16000.xml.i>
<tagNode name="vif-c">
<properties>
@@ -63,12 +64,15 @@
#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>
#include <include/interface/mtu-68-16000.xml.i>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/interface/interface-firewall-vif-c.xml.i>
#include <include/interface/interface-policy-vif-c.xml.i>
</children>
</tagNode>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i
index 11ba7e2f8..4e7f9b3c2 100644
--- a/interface-definitions/include/interface/vif.xml.i
+++ b/interface-definitions/include/interface/vif.xml.i
@@ -18,7 +18,6 @@
#include <include/interface/dhcpv6-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/interface-firewall-vif.xml.i>
#include <include/interface/interface-policy-vif.xml.i>
<leafNode name="egress-qos">
@@ -50,7 +49,10 @@
#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>
#include <include/interface/mtu-68-16000.xml.i>
+ #include <include/interface/redirect.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
<!-- include end -->
diff --git a/interface-definitions/include/ipsec/local-traffic-selector.xml.i b/interface-definitions/include/ipsec/local-traffic-selector.xml.i
index d30a6d11a..9ae67f583 100644
--- a/interface-definitions/include/ipsec/local-traffic-selector.xml.i
+++ b/interface-definitions/include/ipsec/local-traffic-selector.xml.i
@@ -9,11 +9,11 @@
<properties>
<help>Local IPv4 or IPv6 prefix</help>
<valueHelp>
- <format>ipv4</format>
+ <format>ipv4net</format>
<description>Local IPv4 prefix</description>
</valueHelp>
<valueHelp>
- <format>ipv6</format>
+ <format>ipv6net</format>
<description>Local IPv6 prefix</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/isis/high-low-label-value.xml.i b/interface-definitions/include/isis/high-low-label-value.xml.i
new file mode 100644
index 000000000..adc28417d
--- /dev/null
+++ b/interface-definitions/include/isis/high-low-label-value.xml.i
@@ -0,0 +1,26 @@
+<!-- include start from isis/high-low-label-value.xml.i -->
+<leafNode name="low-label-value">
+ <properties>
+ <help>MPLS label lower bound</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>Label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="high-label-value">
+ <properties>
+ <help>MPLS label upper bound</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>Label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/password.xml.i b/interface-definitions/include/isis/password.xml.i
new file mode 100644
index 000000000..27c3b0fa0
--- /dev/null
+++ b/interface-definitions/include/isis/password.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from isis/password.xml.i -->
+<leafNode name="plaintext-password">
+ <properties>
+ <help>Plain-text authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Circuit 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>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i
index 8ffa14a19..75a0355d4 100644
--- a/interface-definitions/include/isis/protocol-common-config.xml.i
+++ b/interface-definitions/include/isis/protocol-common-config.xml.i
@@ -4,24 +4,7 @@
<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>
+ #include <include/isis/password.xml.i>
</children>
</node>
<node name="default-information">
@@ -59,24 +42,7 @@
<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>
+ #include <include/isis/password.xml.i>
</children>
</node>
<leafNode name="dynamic-hostname">
@@ -104,7 +70,7 @@
<description>Act as an area router</description>
</valueHelp>
<constraint>
- <regex>^(level-1|level-1-2|level-2)$</regex>
+ <regex>(level-1|level-1-2|level-2)</regex>
</constraint>
</properties>
</leafNode>
@@ -182,7 +148,7 @@
<description>Use new style of TLVs to carry wider metric</description>
</valueHelp>
<constraint>
- <regex>^(narrow|transition|wide)$</regex>
+ <regex>(narrow|transition|wide)</regex>
</constraint>
</properties>
</leafNode>
@@ -275,68 +241,20 @@
</leafNode>
<node name="global-block">
<properties>
- <help>Global block label range</help>
+ <help>Segment Routing 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>
+ #include <include/isis/high-low-label-value.xml.i>
</children>
</node>
-<!--
<node name="local-block">
<properties>
- <help>Local Block label range</help>
+ <help>Segment Routing 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>
+ #include <include/isis/high-low-label-value.xml.i>
</children>
</node>
--->
<leafNode name="maximum-label-depth">
<properties>
<help>Maximum MPLS labels allowed for this router</help>
@@ -668,7 +586,7 @@
<description>Level-2 only adjacencies are formed</description>
</valueHelp>
<constraint>
- <regex>^(level-1|level-1-2|level-2-only)$</regex>
+ <regex>(level-1|level-1-2|level-2-only)</regex>
</constraint>
</properties>
</leafNode>
@@ -722,15 +640,7 @@
<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>
+ #include <include/isis/password.xml.i>
</children>
</node>
<leafNode name="priority">
diff --git a/interface-definitions/include/nat-port.xml.i b/interface-definitions/include/nat-port.xml.i
index 7aabc33c3..5f762cfb3 100644
--- a/interface-definitions/include/nat-port.xml.i
+++ b/interface-definitions/include/nat-port.xml.i
@@ -3,6 +3,10 @@
<properties>
<help>Port number</help>
<valueHelp>
+ <format>txt</format>
+ <description>Named port (any name in /etc/services, e.g., http)</description>
+ </valueHelp>
+ <valueHelp>
<format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
@@ -14,6 +18,9 @@
<format/>
<description>\n\nMultiple destination ports can be specified as a comma-separated list.\nThe whole list can also be negated using '!'.\nFor example: '!22,telnet,http,123,1001-1005'</description>
</valueHelp>
+ <constraint>
+ <validator name="port-multi"/>
+ </constraint>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/nat-translation-options.xml.i b/interface-definitions/include/nat-translation-options.xml.i
index df2f76397..925f90106 100644
--- a/interface-definitions/include/nat-translation-options.xml.i
+++ b/interface-definitions/include/nat-translation-options.xml.i
@@ -16,13 +16,14 @@
</valueHelp>
<valueHelp>
<format>random</format>
- <description>Random source or destination address allocation for each connection (default)</description>
+ <description>Random source or destination address allocation for each connection</description>
</valueHelp>
<constraint>
<regex>^(persistent|random)$</regex>
</constraint>
</properties>
- </leafNode>
+ <defaultValue>random</defaultValue>
+ </leafNode>
<leafNode name="port-mapping">
<properties>
<help>Port mapping options</help>
@@ -39,13 +40,14 @@
</valueHelp>
<valueHelp>
<format>none</format>
- <description>Do not apply port randomization (default)</description>
+ <description>Do not apply port randomization</description>
</valueHelp>
<constraint>
<regex>^(random|fully-random|none)$</regex>
</constraint>
</properties>
- </leafNode>
+ <defaultValue>none</defaultValue>
+ </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/nat-translation-port.xml.i b/interface-definitions/include/nat-translation-port.xml.i
index 6e507353c..6f17df3d9 100644
--- a/interface-definitions/include/nat-translation-port.xml.i
+++ b/interface-definitions/include/nat-translation-port.xml.i
@@ -10,6 +10,9 @@
<format>range</format>
<description>Numbered port range (e.g., 1001-1005)</description>
</valueHelp>
+ <constraint>
+ <validator name="port-range"/>
+ </constraint>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/ospf/auto-cost.xml.i b/interface-definitions/include/ospf/auto-cost.xml.i
index 3e6cc8232..da6483a00 100644
--- a/interface-definitions/include/ospf/auto-cost.xml.i
+++ b/interface-definitions/include/ospf/auto-cost.xml.i
@@ -6,7 +6,7 @@
<children>
<leafNode name="reference-bandwidth">
<properties>
- <help>Reference bandwidth method to assign cost (default: 100)</help>
+ <help>Reference bandwidth method to assign cost</help>
<valueHelp>
<format>u32:1-4294967</format>
<description>Reference bandwidth cost in Mbits/sec</description>
diff --git a/interface-definitions/include/ospf/interface-common.xml.i b/interface-definitions/include/ospf/interface-common.xml.i
index 738651594..9c8b94f0b 100644
--- a/interface-definitions/include/ospf/interface-common.xml.i
+++ b/interface-definitions/include/ospf/interface-common.xml.i
@@ -20,7 +20,7 @@
</leafNode>
<leafNode name="priority">
<properties>
- <help>Router priority (default: 1)</help>
+ <help>Router priority</help>
<valueHelp>
<format>u32:0-255</format>
<description>OSPF router priority cost</description>
diff --git a/interface-definitions/include/ospf/intervals.xml.i b/interface-definitions/include/ospf/intervals.xml.i
index fad1a6305..9f6e5df69 100644
--- a/interface-definitions/include/ospf/intervals.xml.i
+++ b/interface-definitions/include/ospf/intervals.xml.i
@@ -1,7 +1,7 @@
<!-- include start from ospf/intervals.xml.i -->
<leafNode name="dead-interval">
<properties>
- <help>Interval after which a neighbor is declared dead (default: 40)</help>
+ <help>Interval after which a neighbor is declared dead</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Neighbor dead interval (seconds)</description>
@@ -14,7 +14,7 @@
</leafNode>
<leafNode name="hello-interval">
<properties>
- <help>Interval between hello packets (default: 10)</help>
+ <help>Interval between hello packets</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Hello interval (seconds)</description>
@@ -27,7 +27,7 @@
</leafNode>
<leafNode name="retransmit-interval">
<properties>
- <help>Interval between retransmitting lost link state advertisements (default: 5)</help>
+ <help>Interval between retransmitting lost link state advertisements</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Retransmit interval (seconds)</description>
@@ -40,7 +40,7 @@
</leafNode>
<leafNode name="transmit-delay">
<properties>
- <help>Link state transmit delay (default: 1)</help>
+ <help>Link state transmit delay</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Link state transmit delay (seconds)</description>
diff --git a/interface-definitions/include/ospf/metric-type.xml.i b/interface-definitions/include/ospf/metric-type.xml.i
index ef9fd8ac0..de55c7645 100644
--- a/interface-definitions/include/ospf/metric-type.xml.i
+++ b/interface-definitions/include/ospf/metric-type.xml.i
@@ -1,7 +1,7 @@
<!-- include start from ospf/metric-type.xml.i -->
<leafNode name="metric-type">
<properties>
- <help>OSPF metric type for default routes (default: 2)</help>
+ <help>OSPF metric type for default routes</help>
<valueHelp>
<format>u32:1-2</format>
<description>Set OSPF External Type 1/2 metrics</description>
diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i
index 688e78034..3a3372e47 100644
--- a/interface-definitions/include/ospf/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospf/protocol-common-config.xml.i
@@ -106,7 +106,7 @@
</leafNode>
<leafNode name="translate">
<properties>
- <help>Configure NSSA-ABR (default: candidate)</help>
+ <help>Configure NSSA-ABR</help>
<completionHelp>
<list>always candidate never</list>
</completionHelp>
@@ -116,7 +116,7 @@
</valueHelp>
<valueHelp>
<format>candidate</format>
- <description>Translate for election (default)</description>
+ <description>Translate for election</description>
</valueHelp>
<valueHelp>
<format>never</format>
@@ -256,6 +256,36 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="export-list">
+ <properties>
+ <help>Set the filter for networks announced to other areas</help>
+ <completionHelp>
+ <path>policy access-list</path>
+ </completionHelp>
+ <valueHelp>
+ <format>u32</format>
+ <description>Access-list number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="import-list">
+ <properties>
+ <help>Set the filter for networks from other areas announced</help>
+ <completionHelp>
+ <path>policy access-list</path>
+ </completionHelp>
+ <valueHelp>
+ <format>u32</format>
+ <description>Access-list number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
<tagNode name="virtual-link">
<properties>
<help>Virtual link</help>
@@ -289,6 +319,18 @@
</constraint>
</properties>
</leafNode>
+<leafNode name="maximum-paths">
+ <properties>
+ <help>Maximum multiple paths (ECMP)</help>
+ <valueHelp>
+ <format>u32:1-64</format>
+ <description>Maximum multiple paths (ECMP)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-64"/>
+ </constraint>
+ </properties>
+</leafNode>
<node name="distance">
<properties>
<help>Administrative distance</help>
@@ -490,7 +532,7 @@
<children>
<leafNode name="poll-interval">
<properties>
- <help>Dead neighbor polling interval (default: 60)</help>
+ <help>Dead neighbor polling interval</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Seconds between dead neighbor polling interval</description>
@@ -503,7 +545,7 @@
</leafNode>
<leafNode name="priority">
<properties>
- <help>Neighbor priority in seconds (default: 0)</help>
+ <help>Neighbor priority in seconds</help>
<valueHelp>
<format>u32:0-255</format>
<description>Neighbor priority</description>
@@ -523,13 +565,13 @@
<children>
<leafNode name="abr-type">
<properties>
- <help>OSPF ABR type (default: cisco)</help>
+ <help>OSPF ABR type</help>
<completionHelp>
<list>cisco ibm shortcut standard</list>
</completionHelp>
<valueHelp>
<format>cisco</format>
- <description>Cisco ABR type (default)</description>
+ <description>Cisco ABR type</description>
</valueHelp>
<valueHelp>
<format>ibm</format>
@@ -700,7 +742,7 @@
<children>
<leafNode name="delay">
<properties>
- <help>Delay from the first change received to SPF calculation (default: 200)</help>
+ <help>Delay from the first change received to SPF calculation</help>
<valueHelp>
<format>u32:0-600000</format>
<description>Delay in milliseconds</description>
@@ -713,7 +755,7 @@
</leafNode>
<leafNode name="initial-holdtime">
<properties>
- <help>Initial hold time between consecutive SPF calculations (default: 1000)</help>
+ <help>Initial hold time between consecutive SPF calculations</help>
<valueHelp>
<format>u32:0-600000</format>
<description>Initial hold time in milliseconds</description>
@@ -726,7 +768,7 @@
</leafNode>
<leafNode name="max-holdtime">
<properties>
- <help>Maximum hold time (default: 10000)</help>
+ <help>Maximum hold time</help>
<valueHelp>
<format>u32:0-600000</format>
<description>Max hold time in milliseconds</description>
diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
index 5d08debda..792c873c8 100644
--- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
@@ -158,7 +158,7 @@
</leafNode>
<leafNode name="instance-id">
<properties>
- <help>Instance Id (default: 0)</help>
+ <help>Instance ID</help>
<valueHelp>
<format>u32:0-255</format>
<description>Instance Id</description>
diff --git a/interface-definitions/include/policy/route-common-rule-ipv6.xml.i b/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
index 2d6adcd1d..406125e55 100644
--- a/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
+++ b/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
@@ -232,6 +232,9 @@
<format>!&lt;MAC address&gt;</format>
<description>Match everything except the specified MAC address</description>
</valueHelp>
+ <constraint>
+ <validator name="mac-address-firewall"/>
+ </constraint>
</properties>
</leafNode>
#include <include/firewall/port.xml.i>
@@ -320,26 +323,7 @@
</leafNode>
</children>
</node>
-<node name="tcp">
- <properties>
- <help>TCP flags to match</help>
- </properties>
- <children>
- <leafNode name="flags">
- <properties>
- <help>TCP flags to match</help>
- <valueHelp>
- <format>txt</format>
- <description>TCP flags to match</description>
- </valueHelp>
- <valueHelp>
- <format> </format>
- <description>\n\n Allowed values for TCP flags : SYN ACK FIN RST URG PSH ALL\n When specifying more than one flag, flags should be comma-separated.\n For example : value of 'SYN,!ACK,!FIN,!RST' will only match packets with\n the SYN flag set, and the ACK, FIN and RST flags unset</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
-</node>
+#include <include/firewall/tcp-flags.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
diff --git a/interface-definitions/include/policy/route-common-rule.xml.i b/interface-definitions/include/policy/route-common-rule.xml.i
index c4deefd2a..33c4ba77c 100644
--- a/interface-definitions/include/policy/route-common-rule.xml.i
+++ b/interface-definitions/include/policy/route-common-rule.xml.i
@@ -232,6 +232,9 @@
<format>!&lt;MAC address&gt;</format>
<description>Match everything except the specified MAC address</description>
</valueHelp>
+ <constraint>
+ <validator name="mac-address-firewall"/>
+ </constraint>
</properties>
</leafNode>
#include <include/firewall/port.xml.i>
@@ -320,26 +323,7 @@
</leafNode>
</children>
</node>
-<node name="tcp">
- <properties>
- <help>TCP flags to match</help>
- </properties>
- <children>
- <leafNode name="flags">
- <properties>
- <help>TCP flags to match</help>
- <valueHelp>
- <format>txt</format>
- <description>TCP flags to match</description>
- </valueHelp>
- <valueHelp>
- <format> </format>
- <description>\n\n Allowed values for TCP flags : SYN ACK FIN RST URG PSH ALL\n When specifying more than one flag, flags should be comma-separated.\n For example : value of 'SYN,!ACK,!FIN,!RST' will only match packets with\n the SYN flag set, and the ACK, FIN and RST flags unset</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
-</node>
+#include <include/firewall/tcp-flags.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
diff --git a/interface-definitions/include/qos/bandwidth.xml.i b/interface-definitions/include/qos/bandwidth.xml.i
new file mode 100644
index 000000000..82af22f42
--- /dev/null
+++ b/interface-definitions/include/qos/bandwidth.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from qos/bandwidth.xml.i -->
+<leafNode name="bandwidth">
+ <properties>
+ <help>Traffic-limit used for this class</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Rate in kbit (kilobit per second)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;&lt;suffix&gt;</format>
+ <description>Rate with scaling suffix (mbit, mbps, ...)</description>
+ </valueHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/burst.xml.i b/interface-definitions/include/qos/burst.xml.i
new file mode 100644
index 000000000..761618027
--- /dev/null
+++ b/interface-definitions/include/qos/burst.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/burst.xml.i -->
+<leafNode name="burst">
+ <properties>
+ <help>Burst size for this class</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Bytes</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;&lt;suffix&gt;</format>
+ <description>Bytes with scaling suffix (kb, mb, gb)</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>15k</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/codel-quantum.xml.i b/interface-definitions/include/qos/codel-quantum.xml.i
new file mode 100644
index 000000000..bc24630b6
--- /dev/null
+++ b/interface-definitions/include/qos/codel-quantum.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/codel-quantum.xml.i -->
+<leafNode name="codel-quantum">
+ <properties>
+ <help>Deficit in the fair queuing algorithm</help>
+ <valueHelp>
+ <format>u32:0-1048576</format>
+ <description>Number of bytes used as 'deficit'</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-1048576"/>
+ </constraint>
+ <constraintErrorMessage>Interval must be in range 0 to 1048576</constraintErrorMessage>
+ </properties>
+ <defaultValue>1514</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/dscp.xml.i b/interface-definitions/include/qos/dscp.xml.i
new file mode 100644
index 000000000..bb90850ac
--- /dev/null
+++ b/interface-definitions/include/qos/dscp.xml.i
@@ -0,0 +1,143 @@
+<!-- include start from qos/dscp.xml.i -->
+<leafNode name="dscp">
+ <properties>
+ <help>Match on Differentiated Services Codepoint (DSCP)</help>
+ <completionHelp>
+ <list>default reliability throughput lowdelay priority immediate flash flash-override critical internet network AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43 CS1 CS2 CS3 CS4 CS5 CS6 CS7 EF</list>
+ </completionHelp>
+ <valueHelp>
+ <format>u32:0-63</format>
+ <description>Differentiated Services Codepoint (DSCP) value </description>
+ </valueHelp>
+ <valueHelp>
+ <format>default</format>
+ <description>match DSCP (000000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>reliability</format>
+ <description>match DSCP (000001)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>throughput</format>
+ <description>match DSCP (000010)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>lowdelay</format>
+ <description>match DSCP (000100)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>priority</format>
+ <description>match DSCP (001000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>immediate</format>
+ <description>match DSCP (010000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>flash</format>
+ <description>match DSCP (011000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>flash-override</format>
+ <description>match DSCP (100000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>critical</format>
+ <description>match DSCP (101000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>internet</format>
+ <description>match DSCP (110000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>network</format>
+ <description>match DSCP (111000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF11</format>
+ <description>High-throughput data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF12</format>
+ <description>High-throughput data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF13</format>
+ <description>High-throughput data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF21</format>
+ <description>Low-latency data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF22</format>
+ <description>Low-latency data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF23</format>
+ <description>Low-latency data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF31</format>
+ <description>Multimedia streaming</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF32</format>
+ <description>Multimedia streaming</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF33</format>
+ <description>Multimedia streaming</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF41</format>
+ <description>Multimedia conferencing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF42</format>
+ <description>Multimedia conferencing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>AF43</format>
+ <description>Multimedia conferencing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS1</format>
+ <description>Low-priority data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS2</format>
+ <description>OAM</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS3</format>
+ <description>Broadcast video</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS4</format>
+ <description>Real-time interactive</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS5</format>
+ <description>Signaling</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS6</format>
+ <description>Network control</description>
+ </valueHelp>
+ <valueHelp>
+ <format>CS7</format>
+ <description></description>
+ </valueHelp>
+ <valueHelp>
+ <format>EF</format>
+ <description>Expedited Forwarding</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-63"/>
+ <regex>(default|reliability|throughput|lowdelay|priority|immediate|flash|flash-override|critical|internet|network|AF11|AF12|AF13|AF21|AF22|AF23|AF31|AF32|AF33|AF41|AF42|AF43|CS1|CS2|CS3|CS4|CS5|CS6|CS7|EF)</regex>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 63</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/flows.xml.i b/interface-definitions/include/qos/flows.xml.i
new file mode 100644
index 000000000..a7d7c6422
--- /dev/null
+++ b/interface-definitions/include/qos/flows.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/flows.xml.i -->
+<leafNode name="flows">
+ <properties>
+ <help>Number of flows into which the incoming packets are classified</help>
+ <valueHelp>
+ <format>u32:1-65536</format>
+ <description>Number of flows</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65536"/>
+ </constraint>
+ <constraintErrorMessage>Interval must be in range 1 to 65536</constraintErrorMessage>
+ </properties>
+ <defaultValue>1024</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/hfsc-d.xml.i b/interface-definitions/include/qos/hfsc-d.xml.i
new file mode 100644
index 000000000..2a513509c
--- /dev/null
+++ b/interface-definitions/include/qos/hfsc-d.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from qos/hfsc-d.xml.i -->
+<leafNode name="d">
+ <properties>
+ <help>Service curve delay</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Time in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 65535</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/hfsc-m1.xml.i b/interface-definitions/include/qos/hfsc-m1.xml.i
new file mode 100644
index 000000000..749d01f57
--- /dev/null
+++ b/interface-definitions/include/qos/hfsc-m1.xml.i
@@ -0,0 +1,32 @@
+<!-- include start from qos/hfsc-m1.xml.i -->
+<leafNode name="m1">
+ <properties>
+ <help>Linkshare m1 parameter for class traffic</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Rate in kbit (kilobit per second)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;%%</format>
+ <description>Percentage of overall rate</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bit</format>
+ <description>bit(1), kbit(10^3), mbit(10^6), gbit, tbit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibit</format>
+ <description>kibit(1024), mibit(1024^2), gibit(1024^3), tbit(1024^4)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibps</format>
+ <description>kibps(1024*8), mibps(1024^2*8), gibps, tibps - Byte/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bps</format>
+ <description>bps(8),kbps(8*10^3),mbps(8*10^6), gbps, tbps - Byte/sec</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>100%</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/hfsc-m2.xml.i b/interface-definitions/include/qos/hfsc-m2.xml.i
new file mode 100644
index 000000000..24e8f5d63
--- /dev/null
+++ b/interface-definitions/include/qos/hfsc-m2.xml.i
@@ -0,0 +1,32 @@
+<!-- include start from qos/hfsc-m2.xml.i -->
+<leafNode name="m2">
+ <properties>
+ <help>Linkshare m2 parameter for class traffic</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Rate in kbit (kilobit per second)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;%%</format>
+ <description>Percentage of overall rate</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bit</format>
+ <description>bit(1), kbit(10^3), mbit(10^6), gbit, tbit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibit</format>
+ <description>kibit(1024), mibit(1024^2), gibit(1024^3), tbit(1024^4)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibps</format>
+ <description>kibps(1024*8), mibps(1024^2*8), gibps, tibps - Byte/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bps</format>
+ <description>bps(8),kbps(8*10^3),mbps(8*10^6), gbps, tbps - Byte/sec</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>100%</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/interval.xml.i b/interface-definitions/include/qos/interval.xml.i
new file mode 100644
index 000000000..41896ac9c
--- /dev/null
+++ b/interface-definitions/include/qos/interval.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/interval.xml.i -->
+<leafNode name="interval">
+ <properties>
+ <help>Interval used to measure the delay</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Interval in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ <constraintErrorMessage>Interval must be in range 0 to 4294967295</constraintErrorMessage>
+ </properties>
+ <defaultValue>100</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/match.xml.i b/interface-definitions/include/qos/match.xml.i
new file mode 100644
index 000000000..7d89e4460
--- /dev/null
+++ b/interface-definitions/include/qos/match.xml.i
@@ -0,0 +1,221 @@
+<!-- include start from qos/match.xml.i -->
+<tagNode name="match">
+ <properties>
+ <help>Class matching rule name</help>
+ <constraint>
+ <regex>[^-].*</regex>
+ </constraint>
+ <constraintErrorMessage>Match queue name cannot start with hyphen (-)</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <node name="ether">
+ <properties>
+ <help>Ethernet header match</help>
+ </properties>
+ <children>
+ <leafNode name="destination">
+ <properties>
+ <help>Ethernet destination address for this match</help>
+ <valueHelp>
+ <format>macaddr</format>
+ <description>MAC address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="protocol">
+ <properties>
+ <help>Ethernet protocol for this match</help>
+ <!-- this refers to /etc/protocols -->
+ <completionHelp>
+ <list>all 802.1Q 802_2 802_3 aarp aoe arp atalk dec ip ipv6 ipx lat localtalk rarp snap x25</list>
+ </completionHelp>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Ethernet protocol number</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Ethernet protocol name</description>
+ </valueHelp>
+ <valueHelp>
+ <format>all</format>
+ <description>Any protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ip</format>
+ <description>Internet IP (IPv4)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Internet IP (IPv6)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>arp</format>
+ <description>Address Resolution Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>atalk</format>
+ <description>Appletalk</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipx</format>
+ <description>Novell Internet Packet Exchange</description>
+ </valueHelp>
+ <valueHelp>
+ <format>802.1Q</format>
+ <description>802.1Q VLAN tag</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-protocol"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="source">
+ <properties>
+ <help>Ethernet source address for this match</help>
+ <valueHelp>
+ <format>macaddr</format>
+ <description>MAC address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ #include <include/generic-interface.xml.i>
+ <node name="ip">
+ <properties>
+ <help>Match IP protocol header</help>
+ </properties>
+ <children>
+ <node name="destination">
+ <properties>
+ <help>Match on destination port or address</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IPv4 destination address for this match</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ #include <include/qos/dscp.xml.i>
+ #include <include/qos/max-length.xml.i>
+ #include <include/ip-protocol.xml.i>
+ <node name="source">
+ <properties>
+ <help>Match on source port or address</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IPv4 source address for this match</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ #include <include/qos/tcp-flags.xml.i>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>Match IPv6 protocol header</help>
+ </properties>
+ <children>
+ <node name="destination">
+ <properties>
+ <help>Match on destination port or address</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IPv6 destination address for this match</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ #include <include/qos/dscp.xml.i>
+ #include <include/qos/max-length.xml.i>
+ #include <include/ip-protocol.xml.i>
+ <node name="source">
+ <properties>
+ <help>Match on source port or address</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IPv6 source address for this match</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ #include <include/qos/tcp-flags.xml.i>
+ </children>
+ </node>
+ <leafNode name="mark">
+ <properties>
+ <help>Match on mark applied by firewall</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>FW mark to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0x0-0xffff"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="vif">
+ <properties>
+ <help>Virtual Local Area Network (VLAN) ID for this match</help>
+ <valueHelp>
+ <format>u32:0-4095</format>
+ <description>Virtual Local Area Network (VLAN) tag </description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4095"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 0 and 4095</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/max-length.xml.i b/interface-definitions/include/qos/max-length.xml.i
new file mode 100644
index 000000000..4cc20f8c4
--- /dev/null
+++ b/interface-definitions/include/qos/max-length.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from qos/max-length.xml.i -->
+<leafNode name="max-length">
+ <properties>
+ <help>Maximum packet length (ipv4)</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Maximum packet/payload length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ <constraintErrorMessage>Maximum IPv4 total packet length is 65535</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-limit-1-4294967295.xml.i b/interface-definitions/include/qos/queue-limit-1-4294967295.xml.i
new file mode 100644
index 000000000..2f2d44631
--- /dev/null
+++ b/interface-definitions/include/qos/queue-limit-1-4294967295.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from qos/queue-limit-1-4294967295.xml.i -->
+<leafNode name="queue-limit">
+ <properties>
+ <help>Maximum queue size</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>Queue size in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ <constraintErrorMessage>Queue limit must be greater than zero</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-limit-2-10999.xml.i b/interface-definitions/include/qos/queue-limit-2-10999.xml.i
new file mode 100644
index 000000000..7a9c8266b
--- /dev/null
+++ b/interface-definitions/include/qos/queue-limit-2-10999.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/queue-limit.xml.i -->
+<leafNode name="queue-limit">
+ <properties>
+ <help>Upper limit of the queue</help>
+ <valueHelp>
+ <format>u32:2-10999</format>
+ <description>Queue size in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-10999"/>
+ </constraint>
+ <constraintErrorMessage>Queue limit must greater than 1 and less than 11000</constraintErrorMessage>
+ </properties>
+ <defaultValue>10240</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-type.xml.i b/interface-definitions/include/qos/queue-type.xml.i
new file mode 100644
index 000000000..634f61024
--- /dev/null
+++ b/interface-definitions/include/qos/queue-type.xml.i
@@ -0,0 +1,30 @@
+<!-- include start from qos/queue-type.xml.i -->
+<leafNode name="queue-type">
+ <properties>
+ <help>Queue type for default traffic</help>
+ <completionHelp>
+ <list>fq-codel fair-queue drop-tail random-detect</list>
+ </completionHelp>
+ <valueHelp>
+ <format>fq-codel</format>
+ <description>Fair Queue Codel</description>
+ </valueHelp>
+ <valueHelp>
+ <format>fair-queue</format>
+ <description>Stochastic Fair Queue (SFQ)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>drop-tail</format>
+ <description>First-In-First-Out (FIFO)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>random-detect</format>
+ <description>Random Early Detection (RED)</description>
+ </valueHelp>
+ <constraint>
+ <regex>(fq-codel|fair-queue|drop-tail|random-detect)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>drop-tail</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/set-dscp.xml.i b/interface-definitions/include/qos/set-dscp.xml.i
new file mode 100644
index 000000000..55c0ea44d
--- /dev/null
+++ b/interface-definitions/include/qos/set-dscp.xml.i
@@ -0,0 +1,63 @@
+<!-- include start from qos/set-dscp.xml.i -->
+<leafNode name="set-dscp">
+ <properties>
+ <help>Change the Differentiated Services (DiffServ) field in the IP header</help>
+ <completionHelp>
+ <list>default reliability throughput lowdelay priority immediate flash flash-override critical internet network</list>
+ </completionHelp>
+ <valueHelp>
+ <format>u32:0-63</format>
+ <description>Priority order for bandwidth pool</description>
+ </valueHelp>
+ <valueHelp>
+ <format>default</format>
+ <description>match DSCP (000000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>reliability</format>
+ <description>match DSCP (000001)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>throughput</format>
+ <description>match DSCP (000010)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>lowdelay</format>
+ <description>match DSCP (000100)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>priority</format>
+ <description>match DSCP (001000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>immediate</format>
+ <description>match DSCP (010000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>flash</format>
+ <description>match DSCP (011000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>flash-override</format>
+ <description>match DSCP (100000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>critical</format>
+ <description>match DSCP (101000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>internet</format>
+ <description>match DSCP (110000)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>network</format>
+ <description>match DSCP (111000)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-63"/>
+ <regex>(default|reliability|throughput|lowdelay|priority|immediate|flash|flash-override|critical|internet|network)</regex>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 63</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/target.xml.i b/interface-definitions/include/qos/target.xml.i
new file mode 100644
index 000000000..bf6342ac9
--- /dev/null
+++ b/interface-definitions/include/qos/target.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/target.xml.i -->
+<leafNode name="target">
+ <properties>
+ <help>Acceptable minimum standing/persistent queue delay</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Queue delay in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ <constraintErrorMessage>Delay must be in range 0 to 4294967295</constraintErrorMessage>
+ </properties>
+ <defaultValue>5</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/tcp-flags.xml.i b/interface-definitions/include/qos/tcp-flags.xml.i
new file mode 100644
index 000000000..81d70d1f3
--- /dev/null
+++ b/interface-definitions/include/qos/tcp-flags.xml.i
@@ -0,0 +1,21 @@
+<!-- include start from qos/tcp-flags.xml.i -->
+<node name="tcp">
+ <properties>
+ <help>TCP Flags matching</help>
+ </properties>
+ <children>
+ <leafNode name="ack">
+ <properties>
+ <help>Match TCP ACK</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="syn">
+ <properties>
+ <help>Match TCP SYN</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/radius-server-port.xml.i b/interface-definitions/include/radius-server-port.xml.i
index 4e5d906bc..c6b691a0f 100644
--- a/interface-definitions/include/radius-server-port.xml.i
+++ b/interface-definitions/include/radius-server-port.xml.i
@@ -4,7 +4,7 @@
<help>Authentication port</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>Numeric IP port (default: 1812)</description>
+ <description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
diff --git a/interface-definitions/include/rip/rip-timers.xml.i b/interface-definitions/include/rip/rip-timers.xml.i
index 3aaaf8e65..129d9ed23 100644
--- a/interface-definitions/include/rip/rip-timers.xml.i
+++ b/interface-definitions/include/rip/rip-timers.xml.i
@@ -9,7 +9,7 @@
<help>Garbage collection timer</help>
<valueHelp>
<format>u32:5-2147483647</format>
- <description>Garbage colletion time (default 120)</description>
+ <description>Garbage colletion time</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 5-2147483647"/>
@@ -22,7 +22,7 @@
<help>Routing information timeout timer</help>
<valueHelp>
<format>u32:5-2147483647</format>
- <description>Routing information timeout timer (default 180)</description>
+ <description>Routing information timeout timer</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 5-2147483647"/>
@@ -35,7 +35,7 @@
<help>Routing table update timer</help>
<valueHelp>
<format>u32:5-2147483647</format>
- <description>Routing table update timer in seconds (default 30)</description>
+ <description>Routing table update timer in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 5-2147483647"/>
diff --git a/interface-definitions/include/snmp/access-mode.xml.i b/interface-definitions/include/snmp/access-mode.xml.i
index 1fce2364e..71c766774 100644
--- a/interface-definitions/include/snmp/access-mode.xml.i
+++ b/interface-definitions/include/snmp/access-mode.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
<valueHelp>
<format>ro</format>
- <description>Read-Only (default)</description>
+ <description>Read-Only</description>
</valueHelp>
<valueHelp>
<format>rw</format>
diff --git a/interface-definitions/include/snmp/authentication-type.xml.i b/interface-definitions/include/snmp/authentication-type.xml.i
index 2a545864a..ca0bb10a6 100644
--- a/interface-definitions/include/snmp/authentication-type.xml.i
+++ b/interface-definitions/include/snmp/authentication-type.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
<valueHelp>
<format>md5</format>
- <description>Message Digest 5 (default)</description>
+ <description>Message Digest 5</description>
</valueHelp>
<valueHelp>
<format>sha</format>
diff --git a/interface-definitions/include/snmp/privacy-type.xml.i b/interface-definitions/include/snmp/privacy-type.xml.i
index 47a1e632e..94029a6c6 100644
--- a/interface-definitions/include/snmp/privacy-type.xml.i
+++ b/interface-definitions/include/snmp/privacy-type.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
<valueHelp>
<format>des</format>
- <description>Data Encryption Standard (default)</description>
+ <description>Data Encryption Standard</description>
</valueHelp>
<valueHelp>
<format>aes</format>
diff --git a/interface-definitions/include/snmp/protocol.xml.i b/interface-definitions/include/snmp/protocol.xml.i
index 335736724..ebdeef87e 100644
--- a/interface-definitions/include/snmp/protocol.xml.i
+++ b/interface-definitions/include/snmp/protocol.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
<valueHelp>
<format>udp</format>
- <description>Listen protocol UDP (default)</description>
+ <description>Listen protocol UDP</description>
</valueHelp>
<valueHelp>
<format>tcp</format>
diff --git a/interface-definitions/include/ssh-user.xml.i b/interface-definitions/include/ssh-user.xml.i
index 677602dd8..17ba05a90 100644
--- a/interface-definitions/include/ssh-user.xml.i
+++ b/interface-definitions/include/ssh-user.xml.i
@@ -3,9 +3,9 @@
<properties>
<help>Allow specific users to login</help>
<constraint>
- <regex>[a-z_][a-z0-9_-]{1,31}[$]?</regex>
+ <regex>^[-_a-zA-Z0-9.]{1,100}</regex>
</constraint>
- <constraintErrorMessage>illegal characters or more than 32 characters</constraintErrorMessage>
+ <constraintErrorMessage>Illegal characters or more than 100 characters</constraintErrorMessage>
<multi/>
</properties>
</leafNode>
diff --git a/interface-definitions/include/static/static-route-blackhole.xml.i b/interface-definitions/include/static/static-route-blackhole.xml.i
index f2ad23e69..487f775f5 100644
--- a/interface-definitions/include/static/static-route-blackhole.xml.i
+++ b/interface-definitions/include/static/static-route-blackhole.xml.i
@@ -1,10 +1,11 @@
<!-- include start from static/static-route-blackhole.xml.i -->
<node name="blackhole">
<properties>
- <help>Silently discard packets when matched</help>
+ <help>Silently discard pkts when matched</help>
</properties>
<children>
#include <include/static/static-route-distance.xml.i>
+ #include <include/static/static-route-tag.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/static/static-route-reject.xml.i b/interface-definitions/include/static/static-route-reject.xml.i
new file mode 100644
index 000000000..81d4f9afd
--- /dev/null
+++ b/interface-definitions/include/static/static-route-reject.xml.i
@@ -0,0 +1,12 @@
+<!-- include start from static/static-route-blackhole.xml.i -->
+<node name="reject">
+ <properties>
+ <help>Emit an ICMP unreachable when matched</help>
+ </properties>
+ <children>
+ #include <include/static/static-route-distance.xml.i>
+ #include <include/static/static-route-tag.xml.i>
+ </children>
+</node>
+<!-- include end -->
+
diff --git a/interface-definitions/include/static/static-route-tag.xml.i b/interface-definitions/include/static/static-route-tag.xml.i
new file mode 100644
index 000000000..24bfa732e
--- /dev/null
+++ b/interface-definitions/include/static/static-route-tag.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from static/static-route-tag.xml.i -->
+<leafNode name="tag">
+ <properties>
+ <help>Tag value for this route</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>Tag value for this route</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/static/static-route.xml.i b/interface-definitions/include/static/static-route.xml.i
index 21babc015..2de5dc58f 100644
--- a/interface-definitions/include/static/static-route.xml.i
+++ b/interface-definitions/include/static/static-route.xml.i
@@ -1,7 +1,7 @@
<!-- include start from static/static-route.xml.i -->
<tagNode name="route">
<properties>
- <help>VRF static IPv4 route</help>
+ <help>Static IPv4 route</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 static route</description>
@@ -11,26 +11,8 @@
</constraint>
</properties>
<children>
- <node name="blackhole">
- <properties>
- <help>Silently discard pkts when matched</help>
- </properties>
- <children>
- #include <include/static/static-route-distance.xml.i>
- <leafNode name="tag">
- <properties>
- <help>Tag value for this route</help>
- <valueHelp>
- <format>u32:1-4294967295</format>
- <description>Tag value for this route</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967295"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
+ #include <include/static/static-route-blackhole.xml.i>
+ #include <include/static/static-route-reject.xml.i>
#include <include/dhcp-interface.xml.i>
<tagNode name="interface">
<properties>
diff --git a/interface-definitions/include/static/static-route6.xml.i b/interface-definitions/include/static/static-route6.xml.i
index 0ea995588..35feef41c 100644
--- a/interface-definitions/include/static/static-route6.xml.i
+++ b/interface-definitions/include/static/static-route6.xml.i
@@ -1,7 +1,7 @@
<!-- include start from static/static-route6.xml.i -->
<tagNode name="route6">
<properties>
- <help>VRF static IPv6 route</help>
+ <help>Static IPv6 route</help>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 static route</description>
@@ -11,26 +11,8 @@
</constraint>
</properties>
<children>
- <node name="blackhole">
- <properties>
- <help>Silently discard pkts when matched</help>
- </properties>
- <children>
- #include <include/static/static-route-distance.xml.i>
- <leafNode name="tag">
- <properties>
- <help>Tag value for this route</help>
- <valueHelp>
- <format>u32:1-4294967295</format>
- <description>Tag value for this route</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967295"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
+ #include <include/static/static-route-blackhole.xml.i>
+ #include <include/static/static-route-reject.xml.i>
<tagNode name="interface">
<properties>
<help>IPv6 gateway interface name</help>
diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i
new file mode 100644
index 000000000..15bc5abd4
--- /dev/null
+++ b/interface-definitions/include/version/bgp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/bgp-version.xml.i -->
+<syntaxVersion component='bgp' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/broadcast-relay-version.xml.i b/interface-definitions/include/version/broadcast-relay-version.xml.i
new file mode 100644
index 000000000..98481f446
--- /dev/null
+++ b/interface-definitions/include/version/broadcast-relay-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/broadcast-relay-version.xml.i -->
+<syntaxVersion component='broadcast-relay' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/cluster-version.xml.i b/interface-definitions/include/version/cluster-version.xml.i
new file mode 100644
index 000000000..621996df4
--- /dev/null
+++ b/interface-definitions/include/version/cluster-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/cluster-version.xml.i -->
+<syntaxVersion component='cluster' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/config-management-version.xml.i b/interface-definitions/include/version/config-management-version.xml.i
new file mode 100644
index 000000000..695ba09ab
--- /dev/null
+++ b/interface-definitions/include/version/config-management-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/config-management-version.xml.i -->
+<syntaxVersion component='config-management' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/conntrack-sync-version.xml.i b/interface-definitions/include/version/conntrack-sync-version.xml.i
new file mode 100644
index 000000000..f040c29f6
--- /dev/null
+++ b/interface-definitions/include/version/conntrack-sync-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/conntrack-sync-version.xml.i -->
+<syntaxVersion component='conntrack-sync' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/conntrack-version.xml.i b/interface-definitions/include/version/conntrack-version.xml.i
new file mode 100644
index 000000000..696f76362
--- /dev/null
+++ b/interface-definitions/include/version/conntrack-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/conntrack-version.xml.i -->
+<syntaxVersion component='conntrack' version='3'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/dhcp-relay-version.xml.i b/interface-definitions/include/version/dhcp-relay-version.xml.i
new file mode 100644
index 000000000..75f5d5486
--- /dev/null
+++ b/interface-definitions/include/version/dhcp-relay-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/dhcp-relay-version.xml.i -->
+<syntaxVersion component='dhcp-relay' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/dhcp-server-version.xml.i b/interface-definitions/include/version/dhcp-server-version.xml.i
new file mode 100644
index 000000000..330cb7d1b
--- /dev/null
+++ b/interface-definitions/include/version/dhcp-server-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/dhcp-server-version.xml.i -->
+<syntaxVersion component='dhcp-server' version='6'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/dhcpv6-server-version.xml.i b/interface-definitions/include/version/dhcpv6-server-version.xml.i
new file mode 100644
index 000000000..4b2cf40aa
--- /dev/null
+++ b/interface-definitions/include/version/dhcpv6-server-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/dhcpv6-server-version.xml.i -->
+<syntaxVersion component='dhcpv6-server' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/dns-forwarding-version.xml.i b/interface-definitions/include/version/dns-forwarding-version.xml.i
new file mode 100644
index 000000000..fe817940a
--- /dev/null
+++ b/interface-definitions/include/version/dns-forwarding-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/dns-forwarding-version.xml.i -->
+<syntaxVersion component='dns-forwarding' version='3'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i
new file mode 100644
index 000000000..059a89f24
--- /dev/null
+++ b/interface-definitions/include/version/firewall-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/firewall-version.xml.i -->
+<syntaxVersion component='firewall' version='7'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/flow-accounting-version.xml.i b/interface-definitions/include/version/flow-accounting-version.xml.i
new file mode 100644
index 000000000..5b01fe4b5
--- /dev/null
+++ b/interface-definitions/include/version/flow-accounting-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/flow-accounting-version.xml.i -->
+<syntaxVersion component='flow-accounting' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/https-version.xml.i b/interface-definitions/include/version/https-version.xml.i
new file mode 100644
index 000000000..586083649
--- /dev/null
+++ b/interface-definitions/include/version/https-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/https-version.xml.i -->
+<syntaxVersion component='https' version='3'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/interfaces-version.xml.i b/interface-definitions/include/version/interfaces-version.xml.i
new file mode 100644
index 000000000..b97971531
--- /dev/null
+++ b/interface-definitions/include/version/interfaces-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/interfaces-version.xml.i -->
+<syntaxVersion component='interfaces' version='25'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ipoe-server-version.xml.i b/interface-definitions/include/version/ipoe-server-version.xml.i
new file mode 100644
index 000000000..00d2544e6
--- /dev/null
+++ b/interface-definitions/include/version/ipoe-server-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/ipoe-server-version.xml.i -->
+<syntaxVersion component='ipoe-server' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ipsec-version.xml.i b/interface-definitions/include/version/ipsec-version.xml.i
new file mode 100644
index 000000000..59295cc91
--- /dev/null
+++ b/interface-definitions/include/version/ipsec-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/ipsec-version.xml.i -->
+<syntaxVersion component='ipsec' version='9'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/isis-version.xml.i b/interface-definitions/include/version/isis-version.xml.i
new file mode 100644
index 000000000..4a8fef39c
--- /dev/null
+++ b/interface-definitions/include/version/isis-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/isis-version.xml.i -->
+<syntaxVersion component='isis' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/l2tp-version.xml.i b/interface-definitions/include/version/l2tp-version.xml.i
new file mode 100644
index 000000000..86114d676
--- /dev/null
+++ b/interface-definitions/include/version/l2tp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/l2tp-version.xml.i -->
+<syntaxVersion component='l2tp' version='4'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/lldp-version.xml.i b/interface-definitions/include/version/lldp-version.xml.i
new file mode 100644
index 000000000..0deb73279
--- /dev/null
+++ b/interface-definitions/include/version/lldp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/lldp-version.xml.i -->
+<syntaxVersion component='lldp' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/mdns-version.xml.i b/interface-definitions/include/version/mdns-version.xml.i
new file mode 100644
index 000000000..b200a68b4
--- /dev/null
+++ b/interface-definitions/include/version/mdns-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/mdns-version.xml.i -->
+<syntaxVersion component='mdns' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/nat-version.xml.i b/interface-definitions/include/version/nat-version.xml.i
new file mode 100644
index 000000000..027216a07
--- /dev/null
+++ b/interface-definitions/include/version/nat-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/nat-version.xml.i -->
+<syntaxVersion component='nat' version='5'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/nat66-version.xml.i b/interface-definitions/include/version/nat66-version.xml.i
new file mode 100644
index 000000000..7b7123dcc
--- /dev/null
+++ b/interface-definitions/include/version/nat66-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/nat66-version.xml.i -->
+<syntaxVersion component='nat66' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ntp-version.xml.i b/interface-definitions/include/version/ntp-version.xml.i
new file mode 100644
index 000000000..cc4ff9a1c
--- /dev/null
+++ b/interface-definitions/include/version/ntp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/ntp-version.xml.i -->
+<syntaxVersion component='ntp' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/openconnect-version.xml.i b/interface-definitions/include/version/openconnect-version.xml.i
new file mode 100644
index 000000000..d7d35b321
--- /dev/null
+++ b/interface-definitions/include/version/openconnect-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/openconnect-version.xml.i -->
+<syntaxVersion component='openconnect' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ospf-version.xml.i b/interface-definitions/include/version/ospf-version.xml.i
new file mode 100644
index 000000000..755965daa
--- /dev/null
+++ b/interface-definitions/include/version/ospf-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/ospf-version.xml.i -->
+<syntaxVersion component='ospf' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/policy-version.xml.i b/interface-definitions/include/version/policy-version.xml.i
new file mode 100644
index 000000000..6d0c80518
--- /dev/null
+++ b/interface-definitions/include/version/policy-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/policy-version.xml.i -->
+<syntaxVersion component='policy' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/pppoe-server-version.xml.i b/interface-definitions/include/version/pppoe-server-version.xml.i
new file mode 100644
index 000000000..ec81487f8
--- /dev/null
+++ b/interface-definitions/include/version/pppoe-server-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/pppoe-server-version.xml.i -->
+<syntaxVersion component='pppoe-server' version='5'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/pptp-version.xml.i b/interface-definitions/include/version/pptp-version.xml.i
new file mode 100644
index 000000000..0296c44e9
--- /dev/null
+++ b/interface-definitions/include/version/pptp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/pptp-version.xml.i -->
+<syntaxVersion component='pptp' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/qos-version.xml.i b/interface-definitions/include/version/qos-version.xml.i
new file mode 100644
index 000000000..e4d139349
--- /dev/null
+++ b/interface-definitions/include/version/qos-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/qos-version.xml.i -->
+<syntaxVersion component='qos' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/quagga-version.xml.i b/interface-definitions/include/version/quagga-version.xml.i
new file mode 100644
index 000000000..bb8ad7f82
--- /dev/null
+++ b/interface-definitions/include/version/quagga-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/quagga-version.xml.i -->
+<syntaxVersion component='quagga' version='9'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/rpki-version.xml.i b/interface-definitions/include/version/rpki-version.xml.i
new file mode 100644
index 000000000..2fff259a8
--- /dev/null
+++ b/interface-definitions/include/version/rpki-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/rpki-version.xml.i -->
+<syntaxVersion component='rpki' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/salt-version.xml.i b/interface-definitions/include/version/salt-version.xml.i
new file mode 100644
index 000000000..fe4684050
--- /dev/null
+++ b/interface-definitions/include/version/salt-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/salt-version.xml.i -->
+<syntaxVersion component='salt' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/snmp-version.xml.i b/interface-definitions/include/version/snmp-version.xml.i
new file mode 100644
index 000000000..0416288f0
--- /dev/null
+++ b/interface-definitions/include/version/snmp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/snmp-version.xml.i -->
+<syntaxVersion component='snmp' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ssh-version.xml.i b/interface-definitions/include/version/ssh-version.xml.i
new file mode 100644
index 000000000..0f25caf98
--- /dev/null
+++ b/interface-definitions/include/version/ssh-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/ssh-version.xml.i -->
+<syntaxVersion component='ssh' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/sstp-version.xml.i b/interface-definitions/include/version/sstp-version.xml.i
new file mode 100644
index 000000000..79b43a3e7
--- /dev/null
+++ b/interface-definitions/include/version/sstp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/sstp-version.xml.i -->
+<syntaxVersion component='sstp' version='4'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/system-version.xml.i b/interface-definitions/include/version/system-version.xml.i
new file mode 100644
index 000000000..19591256d
--- /dev/null
+++ b/interface-definitions/include/version/system-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/system-version.xml.i -->
+<syntaxVersion component='system' version='23'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/vrf-version.xml.i b/interface-definitions/include/version/vrf-version.xml.i
new file mode 100644
index 000000000..9d7ff35fe
--- /dev/null
+++ b/interface-definitions/include/version/vrf-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/vrf-version.xml.i -->
+<syntaxVersion component='vrf' version='3'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/vrrp-version.xml.i b/interface-definitions/include/version/vrrp-version.xml.i
new file mode 100644
index 000000000..626dd6cbc
--- /dev/null
+++ b/interface-definitions/include/version/vrrp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/vrrp-version.xml.i -->
+<syntaxVersion component='vrrp' version='3'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/vyos-accel-ppp-version.xml.i b/interface-definitions/include/version/vyos-accel-ppp-version.xml.i
new file mode 100644
index 000000000..e5a4e1613
--- /dev/null
+++ b/interface-definitions/include/version/vyos-accel-ppp-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/vyos-accel-ppp-version.xml.i -->
+<syntaxVersion component='vyos-accel-ppp' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/wanloadbalance-version.xml.i b/interface-definitions/include/version/wanloadbalance-version.xml.i
new file mode 100644
index 000000000..59f8729cc
--- /dev/null
+++ b/interface-definitions/include/version/wanloadbalance-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/wanloadbalance-version.xml.i -->
+<syntaxVersion component='wanloadbalance' version='3'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/webproxy-version.xml.i b/interface-definitions/include/version/webproxy-version.xml.i
new file mode 100644
index 000000000..42dbf3f8b
--- /dev/null
+++ b/interface-definitions/include/version/webproxy-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/webproxy-version.xml.i -->
+<syntaxVersion component='webproxy' version='2'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/vpn-ipsec-encryption.xml.i b/interface-definitions/include/vpn-ipsec-encryption.xml.i
index 9ef2f7c90..eb0678aa9 100644
--- a/interface-definitions/include/vpn-ipsec-encryption.xml.i
+++ b/interface-definitions/include/vpn-ipsec-encryption.xml.i
@@ -11,7 +11,7 @@
</valueHelp>
<valueHelp>
<format>aes128</format>
- <description>128 bit AES-CBC (default)</description>
+ <description>128 bit AES-CBC</description>
</valueHelp>
<valueHelp>
<format>aes192</format>
@@ -229,5 +229,6 @@
<regex>^(null|aes128|aes192|aes256|aes128ctr|aes192ctr|aes256ctr|aes128ccm64|aes192ccm64|aes256ccm64|aes128ccm96|aes192ccm96|aes256ccm96|aes128ccm128|aes192ccm128|aes256ccm128|aes128gcm64|aes192gcm64|aes256gcm64|aes128gcm96|aes192gcm96|aes256gcm96|aes128gcm128|aes192gcm128|aes256gcm128|aes128gmac|aes192gmac|aes256gmac|3des|blowfish128|blowfish192|blowfish256|camellia128|camellia192|camellia256|camellia128ctr|camellia192ctr|camellia256ctr|camellia128ccm64|camellia192ccm64|camellia256ccm64|camellia128ccm96|camellia192ccm96|camellia256ccm96|camellia128ccm128|camellia192ccm128|camellia256ccm128|serpent128|serpent192|serpent256|twofish128|twofish192|twofish256|cast128|chacha20poly1305)$</regex>
</constraint>
</properties>
+ <defaultValue>aes128</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/vpn-ipsec-hash.xml.i b/interface-definitions/include/vpn-ipsec-hash.xml.i
index 5a06b290e..d6259574a 100644
--- a/interface-definitions/include/vpn-ipsec-hash.xml.i
+++ b/interface-definitions/include/vpn-ipsec-hash.xml.i
@@ -15,7 +15,7 @@
</valueHelp>
<valueHelp>
<format>sha1</format>
- <description>SHA1 HMAC (default)</description>
+ <description>SHA1 HMAC</description>
</valueHelp>
<valueHelp>
<format>sha1_160</format>
@@ -61,5 +61,6 @@
<regex>^(md5|md5_128|sha1|sha1_160|sha256|sha256_96|sha384|sha512|aesxcbc|aescmac|aes128gmac|aes192gmac|aes256gmac)$</regex>
</constraint>
</properties>
+ <defaultValue>sha1</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 723041ca5..5ae67a672 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -66,7 +66,7 @@
</completionHelp>
<valueHelp>
<format>layer2</format>
- <description>use MAC addresses to generate the hash (802.3ad, default)</description>
+ <description>use MAC addresses to generate the hash</description>
</valueHelp>
<valueHelp>
<format>layer2+3</format>
@@ -115,7 +115,7 @@
</completionHelp>
<valueHelp>
<format>slow</format>
- <description>Request partner to transmit LACPDUs every 30 seconds (default)</description>
+ <description>Request partner to transmit LACPDUs every 30 seconds</description>
</valueHelp>
<valueHelp>
<format>fast</format>
@@ -135,7 +135,7 @@
</completionHelp>
<valueHelp>
<format>802.3ad</format>
- <description>IEEE 802.3ad Dynamic link aggregation (Default)</description>
+ <description>IEEE 802.3ad Dynamic link aggregation</description>
</valueHelp>
<valueHelp>
<format>active-backup</format>
@@ -207,6 +207,7 @@
</constraint>
</properties>
</leafNode>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
#include <include/interface/xdp.xml.i>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 0856615be..be4c92583 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -26,7 +26,7 @@
</valueHelp>
<valueHelp>
<format>u32:10-1000000</format>
- <description>MAC address aging time in seconds (default: 300)</description>
+ <description>MAC address aging time in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-0 --range 10-1000000"/>
@@ -48,7 +48,7 @@
<help>Forwarding delay</help>
<valueHelp>
<format>u32:0-200</format>
- <description>Spanning Tree Protocol forwarding delay in seconds (default 15)</description>
+ <description>Spanning Tree Protocol forwarding delay in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-200"/>
@@ -59,10 +59,10 @@
</leafNode>
<leafNode name="hello-time">
<properties>
- <help>Hello packet advertisment interval</help>
+ <help>Hello packet advertisement interval</help>
<valueHelp>
<format>u32:1-10</format>
- <description>Spanning Tree Protocol hello advertisement interval in seconds (default 2)</description>
+ <description>Spanning Tree Protocol hello advertisement interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-10"/>
@@ -99,7 +99,7 @@
<help>Interval at which neighbor bridges are removed</help>
<valueHelp>
<format>u32:1-40</format>
- <description>Bridge maximum aging time in seconds (default 20)</description>
+ <description>Bridge maximum aging time in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-40"/>
@@ -195,7 +195,7 @@
<help>Priority for this bridge</help>
<valueHelp>
<format>u32:0-65535</format>
- <description>Bridge priority (default 32768)</description>
+ <description>Bridge priority</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-65535"/>
@@ -210,6 +210,7 @@
<valueless/>
</properties>
</leafNode>
+ #include <include/interface/redirect.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 3bca8b950..7f9ae90e5 100644
--- a/interface-definitions/interfaces-dummy.xml.in
+++ b/interface-definitions/interfaces-dummy.xml.in
@@ -29,7 +29,9 @@
#include <include/interface/source-validation.xml.i>
</children>
</node>
+ #include <include/interface/mirror.xml.i>
#include <include/interface/netns.xml.i>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 9e113cb71..7fa07e9ec 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -41,7 +41,7 @@
</completionHelp>
<valueHelp>
<format>auto</format>
- <description>Auto negotiation (default)</description>
+ <description>Auto negotiation</description>
</valueHelp>
<valueHelp>
<format>half</format>
@@ -110,7 +110,7 @@
</node>
<leafNode name="speed">
<properties>
- <help>Link speed (default: auto)</help>
+ <help>Link speed</help>
<completionHelp>
<list>auto 10 100 1000 2500 5000 10000 25000 40000 50000 100000</list>
</completionHelp>
@@ -196,6 +196,7 @@
</leafNode>
</children>
</node>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
#include <include/interface/vrf.xml.i>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index dd4d324d4..fa5a78be5 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -50,6 +50,8 @@
</node>
</children>
</node>
+ #include <include/interface/mirror.xml.i>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/tunnel-remote.xml.i>
#include <include/vni.xml.i>
</children>
diff --git a/interface-definitions/interfaces-input.xml.in b/interface-definitions/interfaces-input.xml.in
new file mode 100644
index 000000000..2164bfa4e
--- /dev/null
+++ b/interface-definitions/interfaces-input.xml.in
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="interfaces">
+ <children>
+ <tagNode name="input" owner="${vyos_conf_scripts_dir}/interfaces-input.py">
+ <properties>
+ <help>Input Functional Block (IFB) interface name</help>
+ <!-- before real devices that redirect -->
+ <priority>310</priority>
+ <constraint>
+ <regex>ifb[0-9]+</regex>
+ </constraint>
+ <constraintErrorMessage>Input interface must be named ifbN</constraintErrorMessage>
+ <valueHelp>
+ <format>ifbN</format>
+ <description>Input interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/interface/description.xml.i>
+ #include <include/interface/disable.xml.i>
+ #include <include/interface/interface-firewall.xml.i>
+ #include <include/interface/interface-policy.xml.i>
+ #include <include/interface/redirect.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in
index 85d4ab992..1f23a89a5 100644
--- a/interface-definitions/interfaces-l2tpv3.xml.in
+++ b/interface-definitions/interfaces-l2tpv3.xml.in
@@ -20,7 +20,7 @@
#include <include/interface/description.xml.i>
<leafNode name="destination-port">
<properties>
- <help>UDP destination port for L2TPv3 tunnel (default: 5000)</help>
+ <help>UDP destination port for L2TPv3 tunnel</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Numeric IP port</description>
@@ -36,7 +36,7 @@
#include <include/interface/interface-policy.xml.i>
<leafNode name="encapsulation">
<properties>
- <help>Encapsulation type (default: UDP)</help>
+ <help>Encapsulation type</help>
<completionHelp>
<list>udp ip</list>
</completionHelp>
@@ -58,6 +58,7 @@
#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/mirror.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1488</defaultValue>
@@ -86,7 +87,6 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/mtu-68-16000.xml.i>
#include <include/interface/tunnel-remote.xml.i>
<leafNode name="session-id">
<properties>
@@ -102,7 +102,7 @@
</leafNode>
<leafNode name="source-port">
<properties>
- <help>UDP source port for L2TPv3 tunnel (default: 5000)</help>
+ <help>UDP source port for L2TPv3 tunnel</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Numeric IP port</description>
diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in
index 7be15ab89..7ac0545c6 100644
--- a/interface-definitions/interfaces-loopback.xml.in
+++ b/interface-definitions/interfaces-loopback.xml.in
@@ -26,6 +26,8 @@
#include <include/interface/source-validation.xml.i>
</children>
</node>
+ #include <include/interface/mirror.xml.i>
+ #include <include/interface/redirect.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index d69a093af..cb3c489aa 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -16,11 +16,14 @@
</valueHelp>
</properties>
<children>
- #include <include/interface/address-ipv4-ipv6.xml.i>
+ #include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
+ #include <include/interface/dhcp-options.xml.i>
+ #include <include/interface/dhcpv6-options.xml.i>
#include <include/interface/ipv4-options.xml.i>
#include <include/interface/ipv6-options.xml.i>
#include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
+ #include <include/interface/mirror.xml.i>
<node name="security">
<properties>
<help>Security/Encryption Settings</help>
@@ -34,7 +37,7 @@
</completionHelp>
<valueHelp>
<format>gcm-aes-128</format>
- <description>Galois/Counter Mode of AES cipher with 128-bit key (default)</description>
+ <description>Galois/Counter Mode of AES cipher with 128-bit key</description>
</valueHelp>
<valueHelp>
<format>gcm-aes-256</format>
@@ -82,7 +85,7 @@
</leafNode>
<leafNode name="priority">
<properties>
- <help>Priority of MACsec Key Agreement protocol (MKA) actor (default: 255)</help>
+ <help>Priority of MACsec Key Agreement protocol (MKA) actor</help>
<valueHelp>
<format>u32:0-255</format>
<description>MACsec Key Agreement protocol (MKA) priority</description>
@@ -120,6 +123,7 @@
<defaultValue>1460</defaultValue>
</leafNode>
#include <include/source-interface-ethernet.xml.i>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 16d91145f..c917b9312 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -38,7 +38,7 @@
#include <include/interface/interface-policy.xml.i>
<leafNode name="device-type">
<properties>
- <help>OpenVPN interface device-type (default: tun)</help>
+ <help>OpenVPN interface device-type</help>
<completionHelp>
<list>tun tap</list>
</completionHelp>
@@ -168,6 +168,7 @@
</children>
</node>
#include <include/interface/ipv6-options.xml.i>
+ #include <include/interface/mirror.xml.i>
<leafNode name="hash">
<properties>
<help>Hashing Algorithm</help>
@@ -206,7 +207,7 @@
<children>
<leafNode name="failure-count">
<properties>
- <help>Maximum number of keepalive packet failures (default: 60)</help>
+ <help>Maximum number of keepalive packet failures</help>
<valueHelp>
<format>u32:0-1000</format>
<description>Maximum number of keepalive packet failures</description>
@@ -219,7 +220,7 @@
</leafNode>
<leafNode name="interval">
<properties>
- <help>Keepalive packet interval in seconds (default: 10)</help>
+ <help>Keepalive packet interval in seconds</help>
<valueHelp>
<format>u32:0-600</format>
<description>Keepalive packet interval (seconds)</description>
@@ -613,13 +614,13 @@
</leafNode>
<leafNode name="topology">
<properties>
- <help>Topology for clients (default: net30)</help>
+ <help>Topology for clients</help>
<completionHelp>
<list>net30 point-to-point subnet</list>
</completionHelp>
<valueHelp>
<format>net30</format>
- <description>net30 topology (default)</description>
+ <description>net30 topology</description>
</valueHelp>
<valueHelp>
<format>point-to-point</format>
@@ -647,7 +648,7 @@
<children>
<leafNode name="slop">
<properties>
- <help>Maximum allowed clock slop in seconds (default: 180)</help>
+ <help>Maximum allowed clock slop in seconds</help>
<valueHelp>
<format>1-65535</format>
<description>Seconds</description>
@@ -660,7 +661,7 @@
</leafNode>
<leafNode name="drift">
<properties>
- <help>Time drift in seconds (default: 0)</help>
+ <help>Time drift in seconds</help>
<valueHelp>
<format>1-65535</format>
<description>Seconds</description>
@@ -673,7 +674,7 @@
</leafNode>
<leafNode name="step">
<properties>
- <help>Step value for totp in seconds (default: 30)</help>
+ <help>Step value for totp in seconds</help>
<valueHelp>
<format>1-65535</format>
<description>Seconds</description>
@@ -686,7 +687,7 @@
</leafNode>
<leafNode name="digits">
<properties>
- <help>Number of digits to use for totp hash (default: 6)</help>
+ <help>Number of digits to use for totp hash</help>
<valueHelp>
<format>1-65535</format>
<description>Seconds</description>
@@ -699,7 +700,7 @@
</leafNode>
<leafNode name="challenge">
<properties>
- <help>Expect password as result of a challenge response protocol (default: enabled)</help>
+ <help>Expect password as result of a challenge response protocol</help>
<completionHelp>
<list>disable enable</list>
</completionHelp>
@@ -709,7 +710,7 @@
</valueHelp>
<valueHelp>
<format>enable</format>
- <description>Enable chalenge-response (default)</description>
+ <description>Enable chalenge-response</description>
</valueHelp>
<constraint>
<regex>^(disable|enable)$</regex>
@@ -816,6 +817,7 @@
<valueless/>
</properties>
</leafNode>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index 80a890940..3a0b7a40c 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -23,7 +23,7 @@
#include <include/interface/interface-policy.xml.i>
<leafNode name="default-route">
<properties>
- <help>Default route insertion behaviour (default: auto)</help>
+ <help>Default route insertion behaviour</help>
<completionHelp>
<list>auto none force</list>
</completionHelp>
@@ -49,7 +49,6 @@
#include <include/interface/dhcpv6-options.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>
@@ -103,6 +102,7 @@
</constraint>
</properties>
</leafNode>
+ #include <include/interface/mirror.xml.i>
#include <include/interface/mtu-68-1500.xml.i>
<leafNode name="mtu">
<defaultValue>1492</defaultValue>
@@ -134,6 +134,8 @@
<constraintErrorMessage>Service name must be alphanumeric only</constraintErrorMessage>
</properties>
</leafNode>
+ #include <include/interface/redirect.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index bf7055f8d..5f5e9fdef 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -27,6 +27,7 @@
#include <include/interface/ipv6-options.xml.i>
#include <include/source-interface-ethernet.xml.i>
#include <include/interface/mac.xml.i>
+ #include <include/interface/mirror.xml.i>
#include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="mode">
@@ -59,6 +60,7 @@
<defaultValue>private</defaultValue>
</leafNode>
#include <include/interface/mtu-68-16000.xml.i>
+ #include <include/interface/redirect.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 fd69fd177..42ec62775 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -20,7 +20,6 @@
#include <include/interface/address-ipv4-ipv6.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>
@@ -108,6 +107,7 @@
<constraintErrorMessage>Invalid encapsulation, must be one of: erspan, gre, gretap, ip6erspan, ip6gre, ip6gretap, ipip, sit, ipip6 or ip6ip6</constraintErrorMessage>
</properties>
</leafNode>
+ #include <include/interface/mirror.xml.i>
<leafNode name="multicast">
<properties>
<help>Multicast operation over tunnel</help>
@@ -241,7 +241,7 @@
</completionHelp>
<valueHelp>
<format>u32:0-255</format>
- <description>Encapsulation limit (default: 4)</description>
+ <description>Encapsulation limit</description>
</valueHelp>
<valueHelp>
<format>none</format>
@@ -261,7 +261,7 @@
<help>Hoplimit</help>
<valueHelp>
<format>u32:0-255</format>
- <description>Hop limit (default: 64)</description>
+ <description>Hop limit</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-255"/>
@@ -288,6 +288,8 @@
</node>
</children>
</node>
+ #include <include/interface/vrf.xml.i>
+ #include <include/interface/redirect.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-vti.xml.in b/interface-definitions/interfaces-vti.xml.in
index f03c7476d..5893e4c4c 100644
--- a/interface-definitions/interfaces-vti.xml.in
+++ b/interface-definitions/interfaces-vti.xml.in
@@ -34,6 +34,8 @@
#include <include/interface/ipv4-options.xml.i>
#include <include/interface/ipv6-options.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
+ #include <include/interface/mirror.xml.i>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 4c3c3ac71..9747b1816 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -53,6 +53,7 @@
#include <include/interface/ipv6-options.xml.i>
#include <include/interface/mac.xml.i>
#include <include/interface/mtu-1200-16000.xml.i>
+ #include <include/interface/mirror.xml.i>
#include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="mtu">
@@ -98,7 +99,8 @@
</leafNode>
#include <include/source-address-ipv4-ipv6.xml.i>
#include <include/source-interface.xml.i>
- #include <include/interface/tunnel-remote.xml.i>
+ #include <include/interface/tunnel-remote-multi.xml.i>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/vni.xml.i>
</children>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index 7a7c9c1d9..eb0892f07 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -19,11 +19,11 @@
#include <include/interface/address-ipv4-ipv6.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/mtu-68-16000.xml.i>
#include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
+ #include <include/interface/mirror.xml.i>
<leafNode name="mtu">
<defaultValue>1420</defaultValue>
</leafNode>
@@ -101,6 +101,7 @@
</valueHelp>
<constraint>
<validator name="ip-address"/>
+ <validator name="ipv6-link-local"/>
</constraint>
</properties>
</leafNode>
@@ -119,6 +120,8 @@
</leafNode>
</children>
</tagNode>
+ #include <include/interface/redirect.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index a2d1439a3..db01657eb 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -6,6 +6,9 @@
<properties>
<help>Wireless (WiFi/WLAN) Network Interface</help>
<priority>318</priority>
+ <completionHelp>
+ <script>cd /sys/class/net; if compgen -G "wlan*" > /dev/null; then ls -d wlan*; fi</script>
+ </completionHelp>
<constraint>
<regex>^wlan[0-9]+$</regex>
</constraint>
@@ -291,7 +294,7 @@
</completionHelp>
<valueHelp>
<format>0</format>
- <description>20 or 40 MHz channel width (default)</description>
+ <description>20 or 40 MHz channel width</description>
</valueHelp>
<valueHelp>
<format>1</format>
@@ -431,7 +434,7 @@
</node>
<leafNode name="channel">
<properties>
- <help>Wireless radio channel (default: 0)</help>
+ <help>Wireless radio channel</help>
<valueHelp>
<format>0</format>
<description>Automatic Channel Selection (ACS)</description>
@@ -515,7 +518,7 @@
</completionHelp>
<valueHelp>
<format>disabled</format>
- <description>no MFP (hostapd default)</description>
+ <description>no MFP</description>
</valueHelp>
<valueHelp>
<format>optional</format>
@@ -529,6 +532,7 @@
<regex>^(disabled|optional|required)$</regex>
</constraint>
</properties>
+ <defaultValue>disabled</defaultValue>
</leafNode>
<leafNode name="mode">
<properties>
@@ -546,7 +550,7 @@
</valueHelp>
<valueHelp>
<format>g</format>
- <description>802.11g - 54 Mbits/sec (default)</description>
+ <description>802.11g - 54 Mbits/sec</description>
</valueHelp>
<valueHelp>
<format>n</format>
@@ -562,9 +566,10 @@
</properties>
<defaultValue>g</defaultValue>
</leafNode>
+ #include <include/interface/mirror.xml.i>
<leafNode name="physical-device">
<properties>
- <help>Wireless physical device (default: phy0)</help>
+ <help>Wireless physical device</help>
<completionHelp>
<script>${vyos_completion_dir}/list_wireless_phys.sh</script>
</completionHelp>
@@ -777,6 +782,7 @@
</properties>
<defaultValue>monitor</defaultValue>
</leafNode>
+ #include <include/interface/redirect.xml.i>
#include <include/interface/vif.xml.i>
#include <include/interface/vif-s.xml.i>
</children>
diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in
index 03554feed..3cb1645c4 100644
--- a/interface-definitions/interfaces-wwan.xml.in
+++ b/interface-definitions/interfaces-wwan.xml.in
@@ -7,7 +7,7 @@
<help>Wireless Modem (WWAN) Interface</help>
<priority>350</priority>
<completionHelp>
- <script>cd /sys/class/net; ls -d wwan*</script>
+ <script>cd /sys/class/net; if compgen -G "wwan*" > /dev/null; then ls -d wwan*; fi</script>
</completionHelp>
<constraint>
<regex>^wwan[0-9]+$</regex>
@@ -30,8 +30,8 @@
#include <include/interface/authentication.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/mirror.xml.i>
#include <include/interface/mtu-68-1500.xml.i>
<leafNode name="mtu">
<defaultValue>1430</defaultValue>
@@ -41,6 +41,8 @@
#include <include/interface/dial-on-demand.xml.i>
#include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
+ #include <include/interface/redirect.xml.i>
+ #include <include/interface/vrf.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in
index 32ef0ad14..b9ffe234c 100644
--- a/interface-definitions/lldp.xml.in
+++ b/interface-definitions/lldp.xml.in
@@ -28,7 +28,7 @@
#include <include/generic-disable-node.xml.i>
<node name="location">
<properties>
- <help>LLDP-MED location data [REQUIRED]</help>
+ <help>LLDP-MED location data</help>
</properties>
<children>
<node name="coordinate-based">
@@ -40,6 +40,10 @@
<properties>
<help>Altitude in meters</help>
<valueHelp>
+ <format>0</format>
+ <description>No altitude</description>
+ </valueHelp>
+ <valueHelp>
<format>[+-]&lt;meters&gt;</format>
<description>Altitude in meters</description>
</valueHelp>
@@ -48,13 +52,14 @@
<validator name="numeric"/>
</constraint>
</properties>
+ <defaultValue>0</defaultValue>
</leafNode>
<leafNode name="datum">
<properties>
<help>Coordinate datum type</help>
<valueHelp>
<format>WGS84</format>
- <description>WGS84 (default)</description>
+ <description>WGS84</description>
</valueHelp>
<valueHelp>
<format>NAD83</format>
@@ -69,33 +74,34 @@
</completionHelp>
<constraintErrorMessage>Datum should be WGS84, NAD83, or MLLW</constraintErrorMessage>
<constraint>
- <regex>^(WGS84|NAD83|MLLW)$</regex>
+ <regex>(WGS84|NAD83|MLLW)</regex>
</constraint>
</properties>
+ <defaultValue>WGS84</defaultValue>
</leafNode>
<leafNode name="latitude">
<properties>
- <help>Latitude [REQUIRED]</help>
+ <help>Latitude</help>
<valueHelp>
<format>&lt;latitude&gt;</format>
<description>Latitude (example "37.524449N")</description>
</valueHelp>
<constraintErrorMessage>Latitude should be a number followed by S or N</constraintErrorMessage>
<constraint>
- <regex>(\d+)(\.\d+)?[nNsS]$</regex>
+ <regex>(\d+)(\.\d+)?[nNsS]</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="longitude">
<properties>
- <help>Longitude [REQUIRED]</help>
+ <help>Longitude</help>
<valueHelp>
<format>&lt;longitude&gt;</format>
<description>Longitude (example "122.267255W")</description>
</valueHelp>
<constraintErrorMessage>Longiture should be a number followed by E or W</constraintErrorMessage>
<constraint>
- <regex>(\d+)(\.\d+)?[eEwW]$</regex>
+ <regex>(\d+)(\.\d+)?[eEwW]</regex>
</constraint>
</properties>
</leafNode>
@@ -109,7 +115,7 @@
<description>Emergency Call Service ELIN number (between 10-25 numbers)</description>
</valueHelp>
<constraint>
- <regex>[0-9]{10,25}$</regex>
+ <regex>[0-9]{10,25}</regex>
</constraint>
<constraintErrorMessage>ELIN number must be between 10-25 numbers</constraintErrorMessage>
</properties>
diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in
index 86445b65d..573a7963f 100644
--- a/interface-definitions/policy-local-route.xml.in
+++ b/interface-definitions/policy-local-route.xml.in
@@ -14,7 +14,7 @@
<valueHelp>
<!-- table main with prio 32766 -->
<format>u32:1-32765</format>
- <description>Local-route rule number (1-219)</description>
+ <description>Local-route rule number (1-32765)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-32765"/>
@@ -70,6 +70,115 @@
<multi/>
</properties>
</leafNode>
+ <leafNode name="destination">
+ <properties>
+ <help>Destination address or prefix</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Prefix to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ip-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ #include <include/interface/inbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <node name="local-route6" owner="${vyos_conf_scripts_dir}/policy-local-route.py">
+ <properties>
+ <help>IPv6 policy route of local traffic</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv6 policy local-route rule set number</help>
+ <valueHelp>
+ <!-- table main with prio 32766 -->
+ <format>u32:1-32765</format>
+ <description>Local-route rule number (1-32765)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-32765"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="set">
+ <properties>
+ <help>Packet modifications</help>
+ </properties>
+ <children>
+ <leafNode name="table">
+ <properties>
+ <help>Routing table to forward packet with</help>
+ <valueHelp>
+ <format>u32:1-200</format>
+ <description>Table number</description>
+ </valueHelp>
+ <completionHelp>
+ <list>main</list>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="fwmark">
+ <properties>
+ <help>Match fwmark value</help>
+ <valueHelp>
+ <format>u32:1-2147483647</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-2147483647"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="source">
+ <properties>
+ <help>Source address or prefix</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Prefix to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="destination">
+ <properties>
+ <help>Destination address or prefix</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Prefix to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ #include <include/interface/inbound-interface.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/policy-route.xml.in b/interface-definitions/policy-route.xml.in
index ed726d1e4..a1c3b50de 100644
--- a/interface-definitions/policy-route.xml.in
+++ b/interface-definitions/policy-route.xml.in
@@ -2,9 +2,12 @@
<interfaceDefinition>
<node name="policy">
<children>
- <tagNode name="ipv6-route" owner="${vyos_conf_scripts_dir}/policy-route.py">
+ <tagNode name="route6" owner="${vyos_conf_scripts_dir}/policy-route.py">
<properties>
- <help>IPv6 policy route rule set name</help>
+ <help>Policy route rule set name for IPv6</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
<priority>201</priority>
</properties>
<children>
@@ -12,7 +15,15 @@
#include <include/firewall/name-default-log.xml.i>
<tagNode name="rule">
<properties>
- <help>Rule number (1-9999)</help>
+ <help>Policy rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number of policy rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Policy rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
<node name="destination">
@@ -42,7 +53,10 @@
</tagNode>
<tagNode name="route" owner="${vyos_conf_scripts_dir}/policy-route.py">
<properties>
- <help>Policy route rule set name</help>
+ <help>Policy route rule set name for IPv4</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
<priority>201</priority>
</properties>
<children>
@@ -50,7 +64,15 @@
#include <include/firewall/name-default-log.xml.i>
<tagNode name="rule">
<properties>
- <help>Rule number (1-9999)</help>
+ <help>Policy rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number of policy rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Policy rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
<node name="destination">
diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in
index 225f9a6f9..1a4781397 100644
--- a/interface-definitions/policy.xml.in
+++ b/interface-definitions/policy.xml.in
@@ -793,7 +793,7 @@
</node>
<leafNode name="local-preference">
<properties>
- <help>local-preference_help</help>
+ <help>Local Preference</help>
<valueHelp>
<format>u32:0-4294967295</format>
<description>Local Preference</description>
@@ -1086,7 +1086,7 @@
<description>Based on a router-id IP address</description>
</valueHelp>
<constraint>
- <regex>^((?:[0-9]{1,3}\.){3}[0-9]{1,3}|\d+):\d+$</regex>
+ <regex>^(((\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b)|(\d+)):(\d+) ?)+$</regex>
</constraint>
<constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage>
</properties>
@@ -1113,12 +1113,25 @@
<leafNode name="ip-next-hop">
<properties>
<help>Nexthop IP address</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --ipv4</script>
+ <list>unchanged peer-address</list>
+ </completionHelp>
<valueHelp>
<format>ipv4</format>
<description>IP address</description>
</valueHelp>
+ <valueHelp>
+ <format>unchanged</format>
+ <description>Set the BGP nexthop address as unchanged</description>
+ </valueHelp>
+ <valueHelp>
+ <format>peer-address</format>
+ <description>Set the BGP nexthop address to the address of the peer</description>
+ </valueHelp>
<constraint>
<validator name="ipv4-address"/>
+ <regex>^(unchanged|peer-address)$</regex>
</constraint>
</properties>
</leafNode>
@@ -1130,6 +1143,9 @@
<leafNode name="global">
<properties>
<help>Nexthop IPv6 global address</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --ipv6</script>
+ </completionHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address and prefix length</description>
@@ -1142,6 +1158,9 @@
<leafNode name="local">
<properties>
<help>Nexthop IPv6 local address</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --ipv6</script>
+ </completionHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address and prefix length</description>
@@ -1151,6 +1170,12 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="peer-address">
+ <properties>
+ <help>Use peer address (for BGP only)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="prefer-global">
<properties>
<help>Prefer global address as the nexthop</help>
@@ -1268,6 +1293,9 @@
<leafNode name="src">
<properties>
<help>Source address for route</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
+ </completionHelp>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address</description>
diff --git a/interface-definitions/protocols-rpki.xml.in b/interface-definitions/protocols-rpki.xml.in
index a73d0aae4..68762ff9a 100644
--- a/interface-definitions/protocols-rpki.xml.in
+++ b/interface-definitions/protocols-rpki.xml.in
@@ -82,7 +82,7 @@
</tagNode>
<leafNode name="polling-period">
<properties>
- <help>RPKI cache polling period (default: 300)</help>
+ <help>RPKI cache polling period</help>
<valueHelp>
<format>u32:1-86400</format>
<description>Polling period in seconds</description>
diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in
new file mode 100644
index 000000000..e8f575a1e
--- /dev/null
+++ b/interface-definitions/qos.xml.in
@@ -0,0 +1,789 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="qos" owner="${vyos_conf_scripts_dir}/qos.py">
+ <properties>
+ <help>Quality of Service (QoS)</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Interface to apply QoS policy</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="interface-name"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="ingress">
+ <properties>
+ <help>Interface ingress traffic policy</help>
+ <completionHelp>
+ <path>traffic-policy drop-tail</path>
+ <path>traffic-policy fair-queue</path>
+ <path>traffic-policy fq-codel</path>
+ <path>traffic-policy limiter</path>
+ <path>traffic-policy network-emulator</path>
+ <path>traffic-policy priority-queue</path>
+ <path>traffic-policy random-detect</path>
+ <path>traffic-policy rate-control</path>
+ <path>traffic-policy round-robin</path>
+ <path>traffic-policy shaper</path>
+ <path>traffic-policy shaper-hfsc</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>QoS Policy name</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="egress">
+ <properties>
+ <help>Interface egress traffic policy</help>
+ <completionHelp>
+ <path>traffic-policy drop-tail</path>
+ <path>traffic-policy fair-queue</path>
+ <path>traffic-policy fq-codel</path>
+ <path>traffic-policy limiter</path>
+ <path>traffic-policy network-emulator</path>
+ <path>traffic-policy priority-queue</path>
+ <path>traffic-policy random-detect</path>
+ <path>traffic-policy rate-control</path>
+ <path>traffic-policy round-robin</path>
+ <path>traffic-policy shaper</path>
+ <path>traffic-policy shaper-hfsc</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>QoS Policy name</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="policy" owner="${vyos_conf_scripts_dir}/qos.py">
+ <properties>
+ <help>Service Policy definitions</help>
+ <priority>900</priority>
+ </properties>
+ <children>
+ <tagNode name="drop-tail">
+ <properties>
+ <help>Packet limited First In, First Out queue</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/queue-limit-1-4294967295.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="fair-queue">
+ <properties>
+ <help>Stochastic Fairness Queueing</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <leafNode name="hash-interval">
+ <properties>
+ <help>Interval in seconds for queue algorithm perturbation</help>
+ <valueHelp>
+ <format>u32:0</format>
+ <description>No perturbation</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:1-127</format>
+ <description>Interval in seconds for queue algorithm perturbation (advised: 10)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-127"/>
+ </constraint>
+ <constraintErrorMessage>Interval must be in range 0 to 127</constraintErrorMessage>
+ </properties>
+ <defaultValue>0</defaultValue>
+ </leafNode>
+ <leafNode name="queue-limit">
+ <properties>
+ <help>Upper limit of the SFQ</help>
+ <valueHelp>
+ <format>u32:2-127</format>
+ <description>Queue size in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-127"/>
+ </constraint>
+ <constraintErrorMessage>Queue limit must greater than 1 and less than 128</constraintErrorMessage>
+ </properties>
+ <defaultValue>127</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="fq-codel">
+ <properties>
+ <help>Fair Queuing Controlled Delay</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/codel-quantum.xml.i>
+ #include <include/qos/flows.xml.i>
+ #include <include/qos/interval.xml.i>
+ #include <include/qos/queue-limit-2-10999.xml.i>
+ #include <include/qos/target.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="limiter">
+ <properties>
+ <help>Traffic input limiting policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ <tagNode name="class">
+ <properties>
+ <help>Class ID</help>
+ <valueHelp>
+ <format>u32:1-4090</format>
+ <description>Class Identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4090"/>
+ </constraint>
+ <constraintErrorMessage>Class identifier must be between 1 and 4090</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ #include <include/qos/burst.xml.i>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/match.xml.i>
+ <leafNode name="priority">
+ <properties>
+ <help>Priority for rule evaluation</help>
+ <valueHelp>
+ <format>u32:0-20</format>
+ <description>Priority for match rule evaluation</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-20"/>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 20</constraintErrorMessage>
+ </properties>
+ <defaultValue>20</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="default">
+ <properties>
+ <help>Default policy</help>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ #include <include/qos/burst.xml.i>
+ </children>
+ </node>
+ #include <include/generic-description.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="network-emulator">
+ <properties>
+ <help>Network emulator policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ #include <include/qos/burst.xml.i>
+ #include <include/generic-description.xml.i>
+ <leafNode name="network-delay">
+ <properties>
+ <help>Adds delay to packets outgoing to chosen network interface</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Time in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 65535</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="packet-corruption">
+ <properties>
+ <help>Introducing error in a random position for chosen percent of packets</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Percentage of packets affected</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-100"/>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 100</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="packet-loss">
+ <properties>
+ <help>Add independent loss probability to the packets outgoing to chosen network interface</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Percentage of packets affected</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-100"/>
+ </constraint>
+ <constraintErrorMessage>Must be between 0 and 100</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="packet-loss">
+ <properties>
+ <help>Add independent loss probability to the packets outgoing to chosen network interface</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Percentage of packets affected</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-100"/>
+ </constraint>
+ <constraintErrorMessage>Must be between 0 and 100</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="packet-loss">
+ <properties>
+ <help>Packet reordering percentage</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Percentage of packets affected</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-100"/>
+ </constraint>
+ <constraintErrorMessage>Must be between 0 and 100</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ #include <include/qos/queue-limit-1-4294967295.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="priority-queue">
+ <properties>
+ <help>Priority queuing based policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ <tagNode name="class">
+ <properties>
+ <help>Class Handle</help>
+ <valueHelp>
+ <format>u32:1-7</format>
+ <description>Priority</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-7"/>
+ </constraint>
+ <constraintErrorMessage>Class handle must be between 1 and 7</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/codel-quantum.xml.i>
+ #include <include/qos/flows.xml.i>
+ #include <include/qos/interval.xml.i>
+ #include <include/qos/match.xml.i>
+ #include <include/qos/queue-limit-2-10999.xml.i>
+ #include <include/qos/target.xml.i>
+ #include <include/qos/queue-type.xml.i>
+ </children>
+ </tagNode>
+ <node name="default">
+ <properties>
+ <help>Default policy</help>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/codel-quantum.xml.i>
+ #include <include/qos/flows.xml.i>
+ #include <include/qos/interval.xml.i>
+ #include <include/qos/queue-limit-2-10999.xml.i>
+ #include <include/qos/target.xml.i>
+ #include <include/qos/queue-type.xml.i>
+ </children>
+ </node>
+ #include <include/generic-description.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="random-detect">
+ <properties>
+ <help>Priority queuing based policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ <leafNode name="bandwidth">
+ <defaultValue>auto</defaultValue>
+ </leafNode>
+ #include <include/generic-description.xml.i>
+ <tagNode name="precedence">
+ <properties>
+ <help>IP precedence</help>
+ <valueHelp>
+ <format>u32:0-7</format>
+ <description>IP precedence value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-7"/>
+ </constraint>
+ <constraintErrorMessage>IP precedence value must be between 0 and 7</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/queue-limit-1-4294967295.xml.i>
+ <leafNode name="average-packet">
+ <properties>
+ <help>Average packet size (bytes)</help>
+ <valueHelp>
+ <format>u32:16-10240</format>
+ <description>Average packet size in bytes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-100"/>
+ </constraint>
+ <constraintErrorMessage>Average packet size must be between 16 and 10240</constraintErrorMessage>
+ </properties>
+ <defaultValue>1024</defaultValue>
+ </leafNode>
+ <leafNode name="mark-probability">
+ <properties>
+ <help>Mark probability for this precedence</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Numeric value (1/N)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--positive"/>
+ </constraint>
+ <constraintErrorMessage>Mark probability must be greater than 0</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="maximum-threshold">
+ <properties>
+ <help>Maximum threshold for random detection</help>
+ <valueHelp>
+ <format>u32:0-4096</format>
+ <description>Maximum Threshold in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4096"/>
+ </constraint>
+ <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="minimum-threshold">
+ <properties>
+ <help>Minimum threshold for random detection</help>
+ <valueHelp>
+ <format>u32:0-4096</format>
+ <description>Maximum Threshold in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4096"/>
+ </constraint>
+ <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="rate-control">
+ <properties>
+ <help>Rate limiting policy (Token Bucket Filter)</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/burst.xml.i>
+ <leafNode name="latency">
+ <properties>
+ <help>Maximum latency</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Time in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4096"/>
+ </constraint>
+ <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
+ </properties>
+ <defaultValue>50</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="round-robin">
+ <properties>
+ <help>Round-Robin based policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <tagNode name="class">
+ <properties>
+ <help>Class ID</help>
+ <valueHelp>
+ <format>u32:1-4095</format>
+ <description>Class Identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4095"/>
+ </constraint>
+ <constraintErrorMessage>Class identifier must be between 1 and 4095</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/codel-quantum.xml.i>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/flows.xml.i>
+ #include <include/qos/interval.xml.i>
+ #include <include/qos/match.xml.i>
+ <leafNode name="quantum">
+ <properties>
+ <help>Packet scheduling quantum</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>Packet scheduling quantum (bytes)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ <constraintErrorMessage>Quantum must be in range 1 to 4294967295</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ #include <include/qos/queue-limit-1-4294967295.xml.i>
+ #include <include/qos/queue-type.xml.i>
+ #include <include/qos/target.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="shaper-hfsc">
+ <properties>
+ <help>Hierarchical Fair Service Curve's policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ <leafNode name="bandwidth">
+ <defaultValue>auto</defaultValue>
+ </leafNode>
+ #include <include/generic-description.xml.i>
+ <tagNode name="class">
+ <properties>
+ <help>Class ID</help>
+ <valueHelp>
+ <format>u32:1-4095</format>
+ <description>Class Identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4095"/>
+ </constraint>
+ <constraintErrorMessage>Class identifier must be between 1 and 4095</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <node name="linkshare">
+ <properties>
+ <help>Linkshare class settings</help>
+ </properties>
+ <children>
+ #include <include/qos/hfsc-d.xml.i>
+ #include <include/qos/hfsc-m1.xml.i>
+ #include <include/qos/hfsc-m2.xml.i>
+ </children>
+ </node>
+ #include <include/qos/match.xml.i>
+ <node name="realtime">
+ <properties>
+ <help>Realtime class settings</help>
+ </properties>
+ <children>
+ #include <include/qos/hfsc-d.xml.i>
+ #include <include/qos/hfsc-m1.xml.i>
+ #include <include/qos/hfsc-m2.xml.i>
+ </children>
+ </node>
+ <node name="upperlimit">
+ <properties>
+ <help>Upperlimit class settings</help>
+ </properties>
+ <children>
+ #include <include/qos/hfsc-d.xml.i>
+ #include <include/qos/hfsc-m1.xml.i>
+ #include <include/qos/hfsc-m2.xml.i>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ <node name="default">
+ <properties>
+ <help>Default policy</help>
+ </properties>
+ <children>
+ <node name="linkshare">
+ <properties>
+ <help>Linkshare class settings</help>
+ </properties>
+ <children>
+ #include <include/qos/hfsc-d.xml.i>
+ #include <include/qos/hfsc-m1.xml.i>
+ #include <include/qos/hfsc-m2.xml.i>
+ </children>
+ </node>
+ <node name="realtime">
+ <properties>
+ <help>Realtime class settings</help>
+ </properties>
+ <children>
+ #include <include/qos/hfsc-d.xml.i>
+ #include <include/qos/hfsc-m1.xml.i>
+ #include <include/qos/hfsc-m2.xml.i>
+ </children>
+ </node>
+ <node name="upperlimit">
+ <properties>
+ <help>Upperlimit class settings</help>
+ </properties>
+ <children>
+ #include <include/qos/hfsc-d.xml.i>
+ #include <include/qos/hfsc-m1.xml.i>
+ #include <include/qos/hfsc-m2.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ <tagNode name="shaper">
+ <properties>
+ <help>Traffic shaping based policy (Hierarchy Token Bucket)</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Policy name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ <leafNode name="bandwidth">
+ <defaultValue>auto</defaultValue>
+ </leafNode>
+ <tagNode name="class">
+ <properties>
+ <help>Class ID</help>
+ <valueHelp>
+ <format>u32:2-4095</format>
+ <description>Class Identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-4095"/>
+ </constraint>
+ <constraintErrorMessage>Class identifier must be between 2 and 4095</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ <leafNode name="bandwidth">
+ <defaultValue>100%</defaultValue>
+ </leafNode>
+ #include <include/qos/burst.xml.i>
+ <leafNode name="ceiling">
+ <properties>
+ <help>Bandwidth limit for this class</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Rate in kbit (kilobit per second)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;%%</format>
+ <description>Percentage of overall rate</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bit</format>
+ <description>bit(1), kbit(10^3), mbit(10^6), gbit, tbit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibit</format>
+ <description>kibit(1024), mibit(1024^2), gibit(1024^3), tbit(1024^4)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibps</format>
+ <description>kibps(1024*8), mibps(1024^2*8), gibps, tibps - Byte/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bps</format>
+ <description>bps(8),kbps(8*10^3),mbps(8*10^6), gbps, tbps - Byte/sec</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ #include <include/qos/codel-quantum.xml.i>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/flows.xml.i>
+ #include <include/qos/interval.xml.i>
+ #include <include/qos/match.xml.i>
+ <leafNode name="priority">
+ <properties>
+ <help>Priority for usage of excess bandwidth</help>
+ <valueHelp>
+ <format>u32:0-7</format>
+ <description>Priority order for bandwidth pool</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-7"/>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 7</constraintErrorMessage>
+ </properties>
+ <defaultValue>20</defaultValue>
+ </leafNode>
+ #include <include/qos/queue-limit-1-4294967295.xml.i>
+ #include <include/qos/queue-type.xml.i>
+ #include <include/qos/set-dscp.xml.i>
+ #include <include/qos/target.xml.i>
+ </children>
+ </tagNode>
+ #include <include/generic-description.xml.i>
+ <node name="default">
+ <properties>
+ <help>Default policy</help>
+ </properties>
+ <children>
+ #include <include/qos/bandwidth.xml.i>
+ #include <include/qos/burst.xml.i>
+ <leafNode name="ceiling">
+ <properties>
+ <help>Bandwidth limit for this class</help>
+ <valueHelp>
+ <format>&lt;number&gt;</format>
+ <description>Rate in kbit (kilobit per second)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;%%</format>
+ <description>Percentage of overall rate</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bit</format>
+ <description>bit(1), kbit(10^3), mbit(10^6), gbit, tbit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibit</format>
+ <description>kibit(1024), mibit(1024^2), gibit(1024^3), tbit(1024^4)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;ibps</format>
+ <description>kibps(1024*8), mibps(1024^2*8), gibps, tibps - Byte/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;bps</format>
+ <description>bps(8),kbps(8*10^3),mbps(8*10^6), gbps, tbps - Byte/sec</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ #include <include/qos/codel-quantum.xml.i>
+ #include <include/generic-description.xml.i>
+ #include <include/qos/flows.xml.i>
+ #include <include/qos/interval.xml.i>
+ <leafNode name="priority">
+ <properties>
+ <help>Priority for usage of excess bandwidth</help>
+ <valueHelp>
+ <format>u32:0-7</format>
+ <description>Priority order for bandwidth pool</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-7"/>
+ </constraint>
+ <constraintErrorMessage>Priority must be between 0 and 7</constraintErrorMessage>
+ </properties>
+ <defaultValue>20</defaultValue>
+ </leafNode>
+ #include <include/qos/queue-limit-1-4294967295.xml.i>
+ #include <include/qos/queue-type.xml.i>
+ #include <include/qos/set-dscp.xml.i>
+ #include <include/qos/target.xml.i>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/service_console-server.xml.in b/interface-definitions/service_console-server.xml.in
index 28aa7ea71..549edb813 100644
--- a/interface-definitions/service_console-server.xml.in
+++ b/interface-definitions/service_console-server.xml.in
@@ -41,7 +41,7 @@
</leafNode>
<leafNode name="data-bits">
<properties>
- <help>Serial port data bits (default: 8)</help>
+ <help>Serial port data bits</help>
<completionHelp>
<list>7 8</list>
</completionHelp>
@@ -53,7 +53,7 @@
</leafNode>
<leafNode name="stop-bits">
<properties>
- <help>Serial port stop bits (default: 1)</help>
+ <help>Serial port stop bits</help>
<completionHelp>
<list>1 2</list>
</completionHelp>
@@ -65,7 +65,7 @@
</leafNode>
<leafNode name="parity">
<properties>
- <help>Parity setting (default: none)</help>
+ <help>Parity setting</help>
<completionHelp>
<list>even odd none</list>
</completionHelp>
diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in
index b19acab56..1325ba10d 100644
--- a/interface-definitions/service_ipoe-server.xml.in
+++ b/interface-definitions/service_ipoe-server.xml.in
@@ -112,6 +112,22 @@
</children>
</tagNode>
#include <include/name-server-ipv4-ipv6.xml.i>
+ <node name="client-ip-pool">
+ <properties>
+ <help>Client IP pools and gateway setting</help>
+ </properties>
+ <children>
+ <tagNode name="name">
+ <properties>
+ <help>Pool name</help>
+ </properties>
+ <children>
+ #include <include/accel-ppp/gateway-address.xml.i>
+ #include <include/accel-ppp/client-ip-pool-subnet-single.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
#include <include/accel-ppp/client-ipv6-pool.xml.i>
<node name="authentication">
<properties>
diff --git a/interface-definitions/service_monitoring_telegraf.xml.in b/interface-definitions/service_monitoring_telegraf.xml.in
index 0db9052ff..7db9de9f8 100644
--- a/interface-definitions/service_monitoring_telegraf.xml.in
+++ b/interface-definitions/service_monitoring_telegraf.xml.in
@@ -44,19 +44,19 @@
</node>
<leafNode name="bucket">
<properties>
- <help>Remote bucket, by default (main)</help>
+ <help>Remote bucket</help>
</properties>
<defaultValue>main</defaultValue>
</leafNode>
<leafNode name="source">
<properties>
- <help>Source parameters for monitoring (default: all)</help>
+ <help>Source parameters for monitoring</help>
<completionHelp>
<list>all hardware-utilization logs network system telegraf</list>
</completionHelp>
<valueHelp>
<format>all</format>
- <description>All parameters (default)</description>
+ <description>All parameters</description>
</valueHelp>
<valueHelp>
<format>hardware-utilization</format>
@@ -98,10 +98,8 @@
<constraintErrorMessage>Incorrect URL format.</constraintErrorMessage>
</properties>
</leafNode>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Remote port (default: 8086)</help>
- </properties>
<defaultValue>8086</defaultValue>
</leafNode>
</children>
diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in
index 0f4009f5c..ce1da85aa 100644
--- a/interface-definitions/service_router-advert.xml.in
+++ b/interface-definitions/service_router-advert.xml.in
@@ -18,7 +18,7 @@
<children>
<leafNode name="hop-limit">
<properties>
- <help>Set Hop Count field of the IP header for outgoing packets (default: 64)</help>
+ <help>Set Hop Count field of the IP header for outgoing packets</help>
<valueHelp>
<format>u32:0</format>
<description>Unspecified (by this router)</description>
@@ -63,7 +63,7 @@
</valueHelp>
<valueHelp>
<format>medium</format>
- <description>Default router has medium preference (default)</description>
+ <description>Default router has medium preference</description>
</valueHelp>
<valueHelp>
<format>high</format>
@@ -108,7 +108,7 @@
<children>
<leafNode name="max">
<properties>
- <help>Maximum interval between unsolicited multicast RAs (default: 600)</help>
+ <help>Maximum interval between unsolicited multicast RAs</help>
<valueHelp>
<format>u32:4-1800</format>
<description>Maximum interval in seconds</description>
@@ -156,7 +156,7 @@
<children>
<leafNode name="valid-lifetime">
<properties>
- <help>Time in seconds that the route will remain valid (default: 1800 seconds)</help>
+ <help>Time in seconds that the route will remain valid</help>
<completionHelp>
<list>infinity</list>
</completionHelp>
@@ -187,7 +187,7 @@
</valueHelp>
<valueHelp>
<format>medium</format>
- <description>Route has medium preference (default)</description>
+ <description>Route has medium preference</description>
</valueHelp>
<valueHelp>
<format>high</format>
@@ -234,7 +234,7 @@
</leafNode>
<leafNode name="preferred-lifetime">
<properties>
- <help>Time in seconds that the prefix will remain preferred (default 4 hours)</help>
+ <help>Time in seconds that the prefix will remain preferred</help>
<completionHelp>
<list>infinity</list>
</completionHelp>
@@ -255,7 +255,7 @@
</leafNode>
<leafNode name="valid-lifetime">
<properties>
- <help>Time in seconds that the prefix will remain valid (default: 30 days)</help>
+ <help>Time in seconds that the prefix will remain valid</help>
<completionHelp>
<list>infinity</list>
</completionHelp>
diff --git a/interface-definitions/service_upnp.xml.in b/interface-definitions/service_upnp.xml.in
new file mode 100644
index 000000000..7cfe1f02e
--- /dev/null
+++ b/interface-definitions/service_upnp.xml.in
@@ -0,0 +1,224 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="upnp" owner="${vyos_conf_scripts_dir}/service_upnp.py">
+ <properties>
+ <help>Universal Plug and Play (UPnP) service</help>
+ <priority>900</priority>
+ </properties>
+ <children>
+ <leafNode name="friendly-name">
+ <properties>
+ <help>Name of this service</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Friendly name</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="wan-interface">
+ <properties>
+ <help>WAN network interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <constraint>
+ <validator name="interface-name" />
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="wan-ip">
+ <properties>
+ <help>WAN network IP</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address" />
+ <validator name="ipv6-address" />
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="nat-pmp">
+ <properties>
+ <help>Enable NAT-PMP support</help>
+ <valueless />
+ </properties>
+ </leafNode>
+ <leafNode name="secure-mode">
+ <properties>
+ <help>Enable Secure Mode</help>
+ <valueless />
+ </properties>
+ </leafNode>
+ <leafNode name="presentation-url">
+ <properties>
+ <help>Presentation Url</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Presentation Url</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <node name="pcp-lifetime">
+ <properties>
+ <help>PCP-base lifetime Option</help>
+ </properties>
+ <children>
+ <leafNode name="max">
+ <properties>
+ <help>Max lifetime time</help>
+ <constraint>
+ <validator name="numeric" />
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="min">
+ <properties>
+ <help>Min lifetime time</help>
+ <constraint>
+ <validator name="numeric" />
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="listen">
+ <properties>
+ <help>Local IP addresses for service to listen on</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <valueHelp>
+ <format>&lt;interface&gt;</format>
+ <description>Monitor interface address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IP address to listen for incoming connections</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4-prefix</format>
+ <description>IP prefix to listen for incoming connections</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IP address to listen for incoming connections</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6-prefix</format>
+ <description>IP prefix to listen for incoming connections</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="interface-name" />
+ <validator name="ipv4-address"/>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-address"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="stun">
+ <properties>
+ <help>Enable STUN probe support (can be used with NAT 1:1 support for WAN interfaces)</help>
+ </properties>
+ <children>
+ <leafNode name="host">
+ <properties>
+ <help>The STUN server address</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>The STUN server host address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ <tagNode name="rule">
+ <properties>
+ <help>UPnP Rule</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Rule number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ <leafNode name="external-port-range">
+ <properties>
+ <help>Port range (REQUIRE)</help>
+ <valueHelp>
+ <format>&lt;port&gt;</format>
+ <description>single port</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;portN&gt;-&lt;portM&gt;</format>
+ <description>Port range (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="port-range"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="internal-port-range">
+ <properties>
+ <help>Port range (REQUIRE)</help>
+ <valueHelp>
+ <format>&lt;port&gt;</format>
+ <description>single port</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;portN&gt;-&lt;portM&gt;</format>
+ <description>Port range (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="port-range"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="ip">
+ <properties>
+ <help>The IP to which this rule applies (REQUIRE)</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>The IPv4 to which this rule applies</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address" />
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="action">
+ <properties>
+ <help>Actions against the rule (REQUIRE)</help>
+ <completionHelp>
+ <list>allow deny</list>
+ </completionHelp>
+ <constraint>
+ <regex>^(allow|deny)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in
index 03f504ac7..89c4c3910 100644
--- a/interface-definitions/service_webproxy.xml.in
+++ b/interface-definitions/service_webproxy.xml.in
@@ -28,7 +28,7 @@
<children>
<leafNode name="children">
<properties>
- <help>Number of authentication helper processes (default: 5)</help>
+ <help>Number of authentication helper processes</help>
<valueHelp>
<format>n</format>
<description>Number of authentication helper processes</description>
@@ -41,7 +41,7 @@
</leafNode>
<leafNode name="credentials-ttl">
<properties>
- <help>Authenticated session time to live in minutes (default: 60)</help>
+ <help>Authenticated session time to live in minutes</help>
<valueHelp>
<format>n</format>
<description>Authenticated session timeout</description>
@@ -105,7 +105,7 @@
</leafNode>
<leafNode name="version">
<properties>
- <help>LDAP protocol version (default: 3)</help>
+ <help>LDAP protocol version</help>
<completionHelp>
<list>2 3</list>
</completionHelp>
@@ -177,7 +177,7 @@
</leafNode>
<leafNode name="http-port">
<properties>
- <help>Default Proxy Port (default: 3128)</help>
+ <help>Default Proxy Port</help>
<valueHelp>
<format>u32:1025-65535</format>
<description>Default port number</description>
@@ -190,7 +190,11 @@
</leafNode>
<leafNode name="icp-port">
<properties>
- <help>Cache peer ICP port (default: disabled)</help>
+ <help>Cache peer ICP port</help>
+ <valueHelp>
+ <format>u32:0</format>
+ <description>Cache peer disabled</description>
+ </valueHelp>
<valueHelp>
<format>u32:1-65535</format>
<description>Cache peer ICP port</description>
@@ -203,7 +207,7 @@
</leafNode>
<leafNode name="options">
<properties>
- <help>Cache peer options (default: "no-query default")</help>
+ <help>Cache peer options</help>
<valueHelp>
<format>txt</format>
<description>Cache peer options</description>
@@ -239,7 +243,7 @@
</tagNode>
<leafNode name="cache-size">
<properties>
- <help>Disk cache size in MB (default: 100)</help>
+ <help>Disk cache size in MB</help>
<valueHelp>
<format>u32</format>
<description>Disk cache size in MB</description>
@@ -253,7 +257,7 @@
</leafNode>
<leafNode name="default-port">
<properties>
- <help>Default Proxy Port (default: 3128)</help>
+ <help>Default Proxy Port</help>
<valueHelp>
<format>u32:1025-65535</format>
<description>Default port number</description>
@@ -296,7 +300,7 @@
<children>
<leafNode name="port">
<properties>
- <help>Default Proxy Port (default: 3128)</help>
+ <help>Default Proxy Port</help>
<valueHelp>
<format>u32:1025-65535</format>
<description>Default port number</description>
@@ -305,6 +309,7 @@
<validator name="numeric" argument="--range 1025-65535"/>
</constraint>
</properties>
+ <!-- no defaultValue specified as there is default-port -->
</leafNode>
<leafNode name="disable-transparent">
<properties>
@@ -399,7 +404,7 @@
<children>
<leafNode name="update-hour">
<properties>
- <help>Hour of day for database update [REQUIRED]</help>
+ <help>Hour of day for database update</help>
<valueHelp>
<format>u32:0-23</format>
<description>Hour for database update</description>
@@ -414,7 +419,7 @@
</node>
<leafNode name="redirect-url">
<properties>
- <help>Redirect URL for filtered websites (default: block.vyos.net)</help>
+ <help>Redirect URL for filtered websites</help>
<valueHelp>
<format>url</format>
<description>URL for redirect</description>
diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in
index 67d3aef9a..b9e0f4cc5 100644
--- a/interface-definitions/snmp.xml.in
+++ b/interface-definitions/snmp.xml.in
@@ -26,7 +26,7 @@
</completionHelp>
<valueHelp>
<format>ro</format>
- <description>Read-Only (default)</description>
+ <description>Read-Only</description>
</valueHelp>
<valueHelp>
<format>rw</format>
@@ -226,7 +226,7 @@
</valueHelp>
<valueHelp>
<format>auth</format>
- <description>Messages are authenticated but not encrypted (authNoPriv, default)</description>
+ <description>Messages are authenticated but not encrypted (authNoPriv)</description>
</valueHelp>
<valueHelp>
<format>priv</format>
@@ -329,7 +329,7 @@
<list>inform trap</list>
</completionHelp>
<valueHelp>
- <format>inform (default)</format>
+ <format>inform</format>
<description>Use INFORM</description>
</valueHelp>
<valueHelp>
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index e3b9d16e1..8edbad110 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -44,7 +44,7 @@
<list>3des-cbc aes128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com</list>
</completionHelp>
<constraint>
- <regex>^(3des-cbc|aes128-cbc|aes192-cbc|aes256-cbc|rijndael-cbc@lysator.liu.se|aes128-ctr|aes192-ctr|aes256-ctr|aes128-gcm@openssh.com|aes256-gcm@openssh.com|chacha20-poly1305@openssh.com)$</regex>
+ <regex>(3des-cbc|aes128-cbc|aes192-cbc|aes256-cbc|rijndael-cbc@lysator.liu.se|aes128-ctr|aes192-ctr|aes256-ctr|aes128-gcm@openssh.com|aes256-gcm@openssh.com|chacha20-poly1305@openssh.com)</regex>
</constraint>
<multi/>
</properties>
@@ -70,7 +70,7 @@
</completionHelp>
<multi/>
<constraint>
- <regex>^(diffie-hellman-group1-sha1|diffie-hellman-group14-sha1|diffie-hellman-group14-sha256|diffie-hellman-group16-sha512|diffie-hellman-group18-sha512|diffie-hellman-group-exchange-sha1|diffie-hellman-group-exchange-sha256|ecdh-sha2-nistp256|ecdh-sha2-nistp384|ecdh-sha2-nistp521|curve25519-sha256|curve25519-sha256@libssh.org)$</regex>
+ <regex>(diffie-hellman-group1-sha1|diffie-hellman-group14-sha1|diffie-hellman-group14-sha256|diffie-hellman-group16-sha512|diffie-hellman-group18-sha512|diffie-hellman-group-exchange-sha1|diffie-hellman-group-exchange-sha256|ecdh-sha2-nistp256|ecdh-sha2-nistp384|ecdh-sha2-nistp521|curve25519-sha256|curve25519-sha256@libssh.org)</regex>
</constraint>
</properties>
</leafNode>
@@ -102,10 +102,10 @@
<description>enable logging of failed login attempts</description>
</valueHelp>
<constraint>
- <regex>^(quiet|fatal|error|info|verbose)$</regex>
+ <regex>(quiet|fatal|error|info|verbose)</regex>
</constraint>
</properties>
- <defaultValue>INFO</defaultValue>
+ <defaultValue>info</defaultValue>
</leafNode>
<leafNode name="mac">
<properties>
@@ -115,7 +115,7 @@
<list>hmac-sha1 hmac-sha1-96 hmac-sha2-256 hmac-sha2-512 hmac-md5 hmac-md5-96 umac-64@openssh.com umac-128@openssh.com hmac-sha1-etm@openssh.com hmac-sha1-96-etm@openssh.com hmac-sha2-256-etm@openssh.com hmac-sha2-512-etm@openssh.com hmac-md5-etm@openssh.com hmac-md5-96-etm@openssh.com umac-64-etm@openssh.com umac-128-etm@openssh.com</list>
</completionHelp>
<constraint>
- <regex>^(hmac-sha1|hmac-sha1-96|hmac-sha2-256|hmac-sha2-512|hmac-md5|hmac-md5-96|umac-64@openssh.com|umac-128@openssh.com|hmac-sha1-etm@openssh.com|hmac-sha1-96-etm@openssh.com|hmac-sha2-256-etm@openssh.com|hmac-sha2-512-etm@openssh.com|hmac-md5-etm@openssh.com|hmac-md5-96-etm@openssh.com|umac-64-etm@openssh.com|umac-128-etm@openssh.com)$</regex>
+ <regex>(hmac-sha1|hmac-sha1-96|hmac-sha2-256|hmac-sha2-512|hmac-md5|hmac-md5-96|umac-64@openssh.com|umac-128@openssh.com|hmac-sha1-etm@openssh.com|hmac-sha1-96-etm@openssh.com|hmac-sha2-256-etm@openssh.com|hmac-sha2-512-etm@openssh.com|hmac-md5-etm@openssh.com|hmac-md5-96-etm@openssh.com|umac-64-etm@openssh.com|umac-128-etm@openssh.com)</regex>
</constraint>
<multi/>
</properties>
diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in
index daa4177c9..65edab839 100644
--- a/interface-definitions/system-conntrack.xml.in
+++ b/interface-definitions/system-conntrack.xml.in
@@ -35,6 +35,128 @@
</properties>
<defaultValue>32768</defaultValue>
</leafNode>
+ <node name="ignore">
+ <properties>
+ <help>Customized rules to ignore selective connection tracking</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number of conntrack ignore rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/nat-address.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ <leafNode name="inbound-interface">
+ <properties>
+ <help>Interface to ignore connections tracking on</help>
+ <completionHelp>
+ <list>any</list>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ #include <include/ip-protocol.xml.i>
+ <leafNode name="protocol">
+ <properties>
+ <help>Protocol to match (protocol name, number, or "all")</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_protocols.sh</script>
+ <list>all tcp_udp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>all</format>
+ <description>All IP protocols</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tcp_udp</format>
+ <description>Both TCP and UDP</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>IP protocol number</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-protocol"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/nat-address.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <node name="log">
+ <properties>
+ <help>Log connection tracking events per protocol</help>
+ </properties>
+ <children>
+ <node name="icmp">
+ <properties>
+ <help>Log connection tracking events for ICMP</help>
+ </properties>
+ <children>
+ #include <include/conntrack/log-common.xml.i>
+ </children>
+ </node>
+ <node name="other">
+ <properties>
+ <help>Log connection tracking events for all protocols other than TCP, UDP and ICMP</help>
+ </properties>
+ <children>
+ #include <include/conntrack/log-common.xml.i>
+ </children>
+ </node>
+ <node name="tcp">
+ <properties>
+ <help>Log connection tracking events for TCP</help>
+ </properties>
+ <children>
+ #include <include/conntrack/log-common.xml.i>
+ </children>
+ </node>
+ <node name="udp">
+ <properties>
+ <help>Log connection tracking events for UDP</help>
+ </properties>
+ <children>
+ #include <include/conntrack/log-common.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
<node name="modules">
<properties>
<help>Connection tracking modules</help>
@@ -155,176 +277,66 @@
<help>Connection timeout options</help>
</properties>
<children>
- <leafNode name="icmp">
- <properties>
- <help>ICMP timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>ICMP timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>30</defaultValue>
- </leafNode>
- <leafNode name="other">
- <properties>
- <help>Generic connection timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>Generic connection timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>600</defaultValue>
- </leafNode>
- <node name="tcp">
- <properties>
- <help>TCP connection timeout options</help>
- </properties>
- <children>
- <leafNode name="close-wait">
- <properties>
- <help>TCP CLOSE-WAIT timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP CLOSE-WAIT timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>60</defaultValue>
- </leafNode>
- <leafNode name="close">
- <properties>
- <help>TCP CLOSE timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP CLOSE timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>10</defaultValue>
- </leafNode>
- <leafNode name="established">
- <properties>
- <help>TCP ESTABLISHED timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP ESTABLISHED timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>432000</defaultValue>
- </leafNode>
- <leafNode name="fin-wait">
- <properties>
- <help>TCP FIN-WAIT timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP FIN-WAIT timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>120</defaultValue>
- </leafNode>
- <leafNode name="last-ack">
- <properties>
- <help>TCP LAST-ACK timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP LAST-ACK timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>30</defaultValue>
- </leafNode>
- <leafNode name="syn-recv">
- <properties>
- <help>TCP SYN-RECEIVED timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP SYN-RECEIVED timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>60</defaultValue>
- </leafNode>
- <leafNode name="syn-sent">
- <properties>
- <help>TCP SYN-SENT timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP SYN-SENT timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>120</defaultValue>
- </leafNode>
- <leafNode name="time-wait">
- <properties>
- <help>TCP TIME-WAIT timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>TCP TIME-WAIT timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>120</defaultValue>
- </leafNode>
- </children>
- </node>
- <node name="udp">
+ <node name="custom">
<properties>
- <help>UDP timeout options</help>
+ <help>Define custom timeouts per connection</help>
</properties>
<children>
- <leafNode name="other">
+ <tagNode name="rule">
<properties>
- <help>UDP generic timeout in seconds</help>
+ <help>Rule number</help>
<valueHelp>
- <format>u32:1-21474836</format>
- <description>UDP generic timeout in seconds</description>
+ <format>u32:1-999999</format>
+ <description>Number of conntrack rule</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
+ <validator name="numeric" argument="--range 1-999999"/>
</constraint>
+ <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
- <defaultValue>30</defaultValue>
- </leafNode>
- <leafNode name="stream">
- <properties>
- <help>UDP stream timeout in seconds</help>
- <valueHelp>
- <format>u32:1-21474836</format>
- <description>UDP stream timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-21474836"/>
- </constraint>
- </properties>
- <defaultValue>180</defaultValue>
- </leafNode>
+ <children>
+ #include <include/generic-description.xml.i>
+ <node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/nat-address.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ <leafNode name="inbound-interface">
+ <properties>
+ <help>Interface to ignore connections tracking on</help>
+ <completionHelp>
+ <list>any</list>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ #include <include/ip-protocol.xml.i>
+ <node name="protocol">
+ <properties>
+ <help>Customize protocol specific timers, one protocol configuration per rule</help>
+ </properties>
+ <children>
+ #include <include/conntrack/timeout-common-protocols.xml.i>
+ </children>
+ </node>
+ <node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/nat-address.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ </children>
+ </tagNode>
</children>
</node>
+ #include <include/conntrack/timeout-common-protocols.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/system-ip.xml.in b/interface-definitions/system-ip.xml.in
index 86fbe5701..21d70694b 100644
--- a/interface-definitions/system-ip.xml.in
+++ b/interface-definitions/system-ip.xml.in
@@ -5,7 +5,8 @@
<node name="ip" owner="${vyos_conf_scripts_dir}/system-ip.py">
<properties>
<help>IPv4 Settings</help>
- <priority>400</priority>
+ <!-- must be before any interface, check /opt/vyatta/sbin/priority.pl -->
+ <priority>290</priority>
</properties>
<children>
<node name="arp">
@@ -13,18 +14,7 @@
<help>Parameters for ARP cache</help>
</properties>
<children>
- <leafNode name="table-size">
- <properties>
- <help>Maximum number of entries to keep in the ARP cache (default: 8192)</help>
- <completionHelp>
- <list>1024 2048 4096 8192 16384 32768</list>
- </completionHelp>
- <constraint>
- <regex>^(1024|2048|4096|8192|16384|32768)$</regex>
- </constraint>
- </properties>
- <defaultValue>8192</defaultValue>
- </leafNode>
+ #include <include/arp-ndp-table-size.xml.i>
</children>
</node>
<leafNode name="disable-forwarding">
diff --git a/interface-definitions/system-ipv6.xml.in b/interface-definitions/system-ipv6.xml.in
index 5ee7adf54..63260d00c 100644
--- a/interface-definitions/system-ipv6.xml.in
+++ b/interface-definitions/system-ipv6.xml.in
@@ -5,6 +5,7 @@
<node name="ipv6" owner="${vyos_conf_scripts_dir}/system-ipv6.py">
<properties>
<help>IPv6 Settings</help>
+ <!-- must be before any interface, check /opt/vyatta/sbin/priority.pl -->
<priority>290</priority>
</properties>
<children>
@@ -14,12 +15,6 @@
<valueless/>
</properties>
</leafNode>
- <leafNode name="disable">
- <properties>
- <help>Disable assignment of IPv6 addresses on all interfaces</help>
- <valueless/>
- </properties>
- </leafNode>
<node name="multipath">
<properties>
<help>IPv6 multipath settings</help>
@@ -35,20 +30,10 @@
</node>
<node name="neighbor">
<properties>
- <help>Parameters for Neighbor cache</help>
+ <help>Parameters for neighbor discovery cache</help>
</properties>
<children>
- <leafNode name="table-size">
- <properties>
- <help>Maximum number of entries to keep in the Neighbor cache</help>
- <completionHelp>
- <list>1024 2048 4096 8192 16384 32768</list>
- </completionHelp>
- <constraint>
- <regex>^(1024|2048|4096|8192|16384|32768)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/arp-ndp-table-size.xml.i>
</children>
</node>
<leafNode name="strict-dad">
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
index 4bfe82268..a5519ee88 100644
--- a/interface-definitions/system-login.xml.in
+++ b/interface-definitions/system-login.xml.in
@@ -124,7 +124,7 @@
<help>Session timeout</help>
<valueHelp>
<format>u32:1-30</format>
- <description>Session timeout in seconds (default: 2)</description>
+ <description>Session timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-30"/>
@@ -138,7 +138,7 @@
<help>Server priority</help>
<valueHelp>
<format>u32:1-255</format>
- <description>Server priority (default: 255)</description>
+ <description>Server priority</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
diff --git a/interface-definitions/system-logs.xml.in b/interface-definitions/system-logs.xml.in
index 8b6c7c399..1caa7abb6 100644
--- a/interface-definitions/system-logs.xml.in
+++ b/interface-definitions/system-logs.xml.in
@@ -23,7 +23,7 @@
<help>Size of a single log file that triggers rotation</help>
<valueHelp>
<format>u32:1-1024</format>
- <description>Size in MB (default: 10)</description>
+ <description>Size in MB</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-1024" />
@@ -37,7 +37,7 @@
<help>Count of rotations before old logs will be deleted</help>
<valueHelp>
<format>u32:1-100</format>
- <description>Rotations (default: 10)</description>
+ <description>Rotations</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-100" />
@@ -58,7 +58,7 @@
<help>Size of a single log file that triggers rotation</help>
<valueHelp>
<format>u32:1-1024</format>
- <description>Size in MB (default: 1)</description>
+ <description>Size in MB</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-1024" />
@@ -72,7 +72,7 @@
<help>Count of rotations before old logs will be deleted</help>
<valueHelp>
<format>u32:1-100</format>
- <description>Rotations (default: 10)</description>
+ <description>Rotations</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-100" />
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index 0c2205410..a86951ce8 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -30,7 +30,7 @@
</completionHelp>
<valueHelp>
<format>disable</format>
- <description>Disable ESP compression (default)</description>
+ <description>Disable ESP compression</description>
</valueHelp>
<valueHelp>
<format>enable</format>
@@ -47,7 +47,7 @@
<help>ESP lifetime</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>ESP lifetime in seconds (default: 3600)</description>
+ <description>ESP lifetime in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -55,6 +55,30 @@
</properties>
<defaultValue>3600</defaultValue>
</leafNode>
+ <leafNode name="life-bytes">
+ <properties>
+ <help>ESP life in bytes</help>
+ <valueHelp>
+ <format>u32:1024-26843545600000</format>
+ <description>ESP life in bytes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1024-26843545600000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="life-packets">
+ <properties>
+ <help>ESP life in packets</help>
+ <valueHelp>
+ <format>u32:1000-26843545600000</format>
+ <description>ESP life in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1000-26843545600000"/>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="mode">
<properties>
<help>ESP mode</help>
@@ -63,7 +87,7 @@
</completionHelp>
<valueHelp>
<format>tunnel</format>
- <description>Tunnel mode (default)</description>
+ <description>Tunnel mode</description>
</valueHelp>
<valueHelp>
<format>transport</format>
@@ -83,7 +107,7 @@
</completionHelp>
<valueHelp>
<format>enable</format>
- <description>Inherit Diffie-Hellman group from the IKE group (default)</description>
+ <description>Inherit Diffie-Hellman group from the IKE group</description>
</valueHelp>
<valueHelp>
<format>dh-group1</format>
@@ -207,26 +231,22 @@
<properties>
<help>Action to take if a child SA is unexpectedly closed</help>
<completionHelp>
- <list>none hold clear restart</list>
+ <list>none hold restart</list>
</completionHelp>
<valueHelp>
<format>none</format>
- <description>Do nothing (default)</description>
+ <description>Do nothing</description>
</valueHelp>
<valueHelp>
<format>hold</format>
<description>Attempt to re-negotiate when matching traffic is seen</description>
</valueHelp>
<valueHelp>
- <format>clear</format>
- <description>Remove the connection immediately</description>
- </valueHelp>
- <valueHelp>
<format>restart</format>
<description>Attempt to re-negotiate the connection immediately</description>
</valueHelp>
<constraint>
- <regex>^(none|hold|clear|restart)$</regex>
+ <regex>^(none|hold|restart)$</regex>
</constraint>
</properties>
</leafNode>
@@ -243,7 +263,7 @@
</completionHelp>
<valueHelp>
<format>hold</format>
- <description>Attempt to re-negotiate the connection when matching traffic is seen (default)</description>
+ <description>Attempt to re-negotiate the connection when matching traffic is seen</description>
</valueHelp>
<valueHelp>
<format>clear</format>
@@ -263,30 +283,32 @@
<help>Keep-alive interval</help>
<valueHelp>
<format>u32:2-86400</format>
- <description>Keep-alive interval in seconds (default: 30)</description>
+ <description>Keep-alive interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 2-86400"/>
</constraint>
</properties>
+ <defaultValue>30</defaultValue>
</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>
+ <description>Keep-alive timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 2-86400"/>
</constraint>
</properties>
+ <defaultValue>120</defaultValue>
</leafNode>
</children>
</node>
<leafNode name="ikev2-reauth">
<properties>
- <help>ikev2-reauth_help</help>
+ <help>Re-authentication of the remote peer during an IKE re-key - IKEv2 only</help>
<completionHelp>
<list>yes no</list>
</completionHelp>
@@ -296,7 +318,7 @@
</valueHelp>
<valueHelp>
<format>no</format>
- <description>Disable remote host re-authenticaton during an IKE rekey. (default)</description>
+ <description>Disable remote host re-authenticaton during an IKE rekey</description>
</valueHelp>
<constraint>
<regex>^(yes|no)$</regex>
@@ -311,7 +333,7 @@
</completionHelp>
<valueHelp>
<format>ikev1</format>
- <description>Use IKEv1 for key exchange [DEFAULT]</description>
+ <description>Use IKEv1 for key exchange</description>
</valueHelp>
<valueHelp>
<format>ikev2</format>
@@ -327,7 +349,7 @@
<help>IKE lifetime</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>IKE lifetime in seconds (default: 28800)</description>
+ <description>IKE lifetime in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -343,7 +365,7 @@
</completionHelp>
<valueHelp>
<format>enable</format>
- <description>Enable MOBIKE (default for IKEv2)</description>
+ <description>Enable MOBIKE</description>
</valueHelp>
<valueHelp>
<format>disable</format>
@@ -353,6 +375,7 @@
<regex>^(enable|disable)$</regex>
</constraint>
</properties>
+ <defaultValue>enable</defaultValue>
</leafNode>
<leafNode name="mode">
<properties>
@@ -362,7 +385,7 @@
</completionHelp>
<valueHelp>
<format>main</format>
- <description>Use the main mode (recommended, default)</description>
+ <description>Use the main mode (recommended)</description>
</valueHelp>
<valueHelp>
<format>aggressive</format>
@@ -372,6 +395,7 @@
<regex>^(main|aggressive)$</regex>
</constraint>
</properties>
+ <defaultValue>main</defaultValue>
</leafNode>
<tagNode name="proposal">
<properties>
@@ -509,7 +533,7 @@
<help>strongSwan logging Level</help>
<valueHelp>
<format>0</format>
- <description>Very basic auditing logs e.g. SA up/SA down (default)</description>
+ <description>Very basic auditing logs e.g. SA up/SA down</description>
</valueHelp>
<valueHelp>
<format>1</format>
@@ -622,6 +646,19 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="flexvpn">
+ <properties>
+ <help>Allow FlexVPN vendor ID payload (IKEv2 only)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/generic-interface.xml.i>
+ <leafNode name="virtual-ip">
+ <properties>
+ <help>Allow install virtual-ip addresses</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
<tagNode name="profile">
@@ -754,7 +791,7 @@
</valueHelp>
<valueHelp>
<format>u32:1-86400</format>
- <description>Timeout in seconds (default: 28800)</description>
+ <description>Timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-86400"/>
@@ -838,11 +875,11 @@
<properties>
<help>Local IPv4 or IPv6 pool prefix exclusions</help>
<valueHelp>
- <format>ipv4</format>
+ <format>ipv4net</format>
<description>Local IPv4 pool prefix exclusion</description>
</valueHelp>
<valueHelp>
- <format>ipv6</format>
+ <format>ipv6net</format>
<description>Local IPv6 pool prefix exclusion</description>
</valueHelp>
<constraint>
@@ -856,11 +893,11 @@
<properties>
<help>Local IPv4 or IPv6 pool prefix</help>
<valueHelp>
- <format>ipv4</format>
+ <format>ipv4net</format>
<description>Local IPv4 pool prefix</description>
</valueHelp>
<valueHelp>
- <format>ipv6</format>
+ <format>ipv6net</format>
<description>Local IPv6 pool prefix</description>
</valueHelp>
<constraint>
@@ -965,7 +1002,7 @@
<properties>
<help>Connection type</help>
<completionHelp>
- <list>initiate respond</list>
+ <list>initiate respond none</list>
</completionHelp>
<valueHelp>
<format>initiate</format>
@@ -975,8 +1012,12 @@
<format>respond</format>
<description>Bring the connection up only if traffic is detected</description>
</valueHelp>
+ <valueHelp>
+ <format>none</format>
+ <description>Load the connection only</description>
+ </valueHelp>
<constraint>
- <regex>^(initiate|respond)$</regex>
+ <regex>^(initiate|respond|none)$</regex>
</constraint>
</properties>
</leafNode>
@@ -1026,7 +1067,7 @@
</valueHelp>
<valueHelp>
<format>inherit</format>
- <description>Inherit the reauth configuration form your IKE-group (default)</description>
+ <description>Inherit the reauth configuration form your IKE-group</description>
</valueHelp>
<constraint>
<regex>^(yes|no|inherit)$</regex>
@@ -1069,11 +1110,11 @@
<properties>
<help>Remote IPv4 or IPv6 prefix</help>
<valueHelp>
- <format>ipv4</format>
+ <format>ipv4net</format>
<description>Remote IPv4 prefix</description>
</valueHelp>
<valueHelp>
- <format>ipv6</format>
+ <format>ipv6net</format>
<description>Remote IPv6 prefix</description>
</valueHelp>
<constraint>
@@ -1087,6 +1128,20 @@
</node>
</children>
</tagNode>
+ <leafNode name="virtual-address">
+ <properties>
+ <help>Initiator request virtual-address from peer</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Request IPv4 address from peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Request IPv6 address from peer</description>
+ </valueHelp>
+ <multi/>
+ </properties>
+ </leafNode>
<node name="vti">
<properties>
<help>Virtual tunnel interface [REQUIRED]</help>
diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in
index 6a88756a7..9ca7b1fad 100644
--- a/interface-definitions/vpn_l2tp.xml.in
+++ b/interface-definitions/vpn_l2tp.xml.in
@@ -88,7 +88,7 @@
<help>IKE lifetime</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>IKE lifetime in seconds (default 3600)</description>
+ <description>IKE lifetime in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -101,7 +101,7 @@
<help>ESP lifetime</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>IKE lifetime in seconds (default 3600)</description>
+ <description>IKE lifetime in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -135,7 +135,7 @@
<help>PPP idle timeout</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>PPP idle timeout in seconds (default 1800)</description>
+ <description>PPP idle timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -206,7 +206,7 @@
</leafNode>
<leafNode name="acct-timeout">
<properties>
- <help>Timeout to wait reply for Interim-Update packets. (default 3 seconds)</help>
+ <help>Timeout to wait reply for Interim-Update packets</help>
</properties>
</leafNode>
<leafNode name="max-try">
@@ -244,7 +244,7 @@
<children>
<leafNode name="attribute">
<properties>
- <help>Specifies which radius attribute contains rate information. (default is Filter-Id)</help>
+ <help>Specifies which radius attribute contains rate information</help>
</properties>
</leafNode>
<leafNode name="vendor">
diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in
index 0db5e79d0..f418f5d75 100644
--- a/interface-definitions/vpn_openconnect.xml.in
+++ b/interface-definitions/vpn_openconnect.xml.in
@@ -40,13 +40,13 @@
<properties>
<help>Session timeout</help>
<valueHelp>
- <format>u32:1-30</format>
+ <format>u32:1-240</format>
<description>Session timeout in seconds (default: 2)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-30"/>
+ <validator name="numeric" argument="--range 1-240"/>
</constraint>
- <constraintErrorMessage>Timeout must be between 1 and 30 seconds</constraintErrorMessage>
+ <constraintErrorMessage>Timeout must be between 1 and 240 seconds</constraintErrorMessage>
</properties>
<defaultValue>2</defaultValue>
</leafNode>
@@ -61,10 +61,10 @@
<children>
<leafNode name="tcp">
<properties>
- <help>tcp port number to accept connections (default: 443)</help>
+ <help>tcp port number to accept connections</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>Numeric IP port (default: 443)</description>
+ <description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
@@ -74,10 +74,10 @@
</leafNode>
<leafNode name="udp">
<properties>
- <help>udp port number to accept connections (default: 443)</help>
+ <help>udp port number to accept connections</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>Numeric IP port (default: 443)</description>
+ <description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
@@ -160,7 +160,7 @@
<help>Prefix length used for individual client</help>
<valueHelp>
<format>u32:48-128</format>
- <description>Client prefix length (default: 64)</description>
+ <description>Client prefix length</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 48-128"/>
diff --git a/interface-definitions/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in
new file mode 100644
index 000000000..b7f063a6c
--- /dev/null
+++ b/interface-definitions/xml-component-version.xml.in
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ #include <include/version/bgp-version.xml.i>
+ #include <include/version/broadcast-relay-version.xml.i>
+ #include <include/version/cluster-version.xml.i>
+ #include <include/version/config-management-version.xml.i>
+ #include <include/version/conntrack-sync-version.xml.i>
+ #include <include/version/conntrack-version.xml.i>
+ #include <include/version/dhcp-relay-version.xml.i>
+ #include <include/version/dhcp-server-version.xml.i>
+ #include <include/version/dhcpv6-server-version.xml.i>
+ #include <include/version/dns-forwarding-version.xml.i>
+ #include <include/version/firewall-version.xml.i>
+ #include <include/version/flow-accounting-version.xml.i>
+ #include <include/version/https-version.xml.i>
+ #include <include/version/interfaces-version.xml.i>
+ #include <include/version/ipoe-server-version.xml.i>
+ #include <include/version/ipsec-version.xml.i>
+ #include <include/version/isis-version.xml.i>
+ #include <include/version/l2tp-version.xml.i>
+ #include <include/version/lldp-version.xml.i>
+ #include <include/version/mdns-version.xml.i>
+ #include <include/version/nat66-version.xml.i>
+ #include <include/version/nat-version.xml.i>
+ #include <include/version/ntp-version.xml.i>
+ #include <include/version/openconnect-version.xml.i>
+ #include <include/version/ospf-version.xml.i>
+ #include <include/version/policy-version.xml.i>
+ #include <include/version/pppoe-server-version.xml.i>
+ #include <include/version/pptp-version.xml.i>
+ #include <include/version/qos-version.xml.i>
+ #include <include/version/quagga-version.xml.i>
+ #include <include/version/rpki-version.xml.i>
+ #include <include/version/salt-version.xml.i>
+ #include <include/version/snmp-version.xml.i>
+ #include <include/version/ssh-version.xml.i>
+ #include <include/version/sstp-version.xml.i>
+ #include <include/version/system-version.xml.i>
+ #include <include/version/vrf-version.xml.i>
+ #include <include/version/vrrp-version.xml.i>
+ #include <include/version/vyos-accel-ppp-version.xml.i>
+ #include <include/version/wanloadbalance-version.xml.i>
+ #include <include/version/webproxy-version.xml.i>
+</interfaceDefinition>
diff --git a/interface-definitions/zone-policy.xml.in b/interface-definitions/zone-policy.xml.in
index dd64c7c16..eac63fa6b 100644
--- a/interface-definitions/zone-policy.xml.in
+++ b/interface-definitions/zone-policy.xml.in
@@ -13,6 +13,9 @@
<format>txt</format>
<description>Zone name</description>
</valueHelp>
+ <constraint>
+ <regex>^[a-zA-Z0-9][\w\-\.]*$</regex>
+ </constraint>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -24,7 +27,7 @@
</completionHelp>
<valueHelp>
<format>drop</format>
- <description>Drop silently (default)</description>
+ <description>Drop silently</description>
</valueHelp>
<valueHelp>
<format>reject</format>
@@ -34,6 +37,7 @@
<regex>^(drop|reject)$</regex>
</constraint>
</properties>
+ <defaultValue>drop</defaultValue>
</leafNode>
<tagNode name="from">
<properties>
@@ -94,7 +98,7 @@
</completionHelp>
<valueHelp>
<format>accept</format>
- <description>Accept traffic (default)</description>
+ <description>Accept traffic</description>
</valueHelp>
<valueHelp>
<format>drop</format>
@@ -135,7 +139,7 @@
<help>Zone to be local-zone</help>
<valueless/>
</properties>
- </leafNode>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/op-mode-definitions/generate-openvpn-config-client.xml.in b/op-mode-definitions/generate-openvpn-config-client.xml.in
new file mode 100644
index 000000000..4f9f31bfe
--- /dev/null
+++ b/op-mode-definitions/generate-openvpn-config-client.xml.in
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="generate">
+ <children>
+ <node name="openvpn">
+ <properties>
+ <help>Generate OpenVPN client configuration ovpn file</help>
+ </properties>
+ <children>
+ <node name="client-config">
+ <properties>
+ <help>Generate Client config</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Local interface used for connection</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py --type openvpn</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="ca">
+ <properties>
+ <help>CA certificate</help>
+ <completionHelp>
+ <path>pki ca</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="certificate">
+ <properties>
+ <help>Cerificate used by client</help>
+ <completionHelp>
+ <path>pki certificate</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="key">
+ <properties>
+ <help>Certificate key used by client</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/generate_ovpn_client_file.py --interface "$5" --ca "$7" --cert "$9" --key "${11}"</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/generate_ovpn_client_file.py --interface "$5" --ca "$7" --cert "$9"</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-flowspec.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-flowspec.xml.i
new file mode 100644
index 000000000..34228fdd1
--- /dev/null
+++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-flowspec.xml.i
@@ -0,0 +1,25 @@
+<!-- included start from bgp/afi-ipv4-ipv6-flowspec.xml.i -->
+<tagNode name="flowspec">
+ <properties>
+ <help>Network in the BGP routing table to display</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt; &lt;x.x.x.x/x&gt; &lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <children>
+ #include <include/bgp/prefix-bestpath-multipath.xml.i>
+ </children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<node name="flowspec">
+ <properties>
+ <help>Flowspec Address Family modifier</help>
+ </properties>
+ <children>
+ #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/afi-ipv4-ipv6-common.xml.i>
+ #include <include/vtysh-generic-detail.xml.i>
+ </children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</node>
+<!-- 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 e81b26b3e..c9a112fca 100644
--- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i
+++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i
@@ -20,6 +20,7 @@
<children>
#include <include/bgp/afi-common.xml.i>
#include <include/bgp/afi-ipv4-ipv6-common.xml.i>
+ #include <include/bgp/afi-ipv4-ipv6-flowspec.xml.i>
#include <include/bgp/afi-ipv4-ipv6-vpn.xml.i>
</children>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
diff --git a/op-mode-definitions/include/ospf-common.xml.i b/op-mode-definitions/include/ospf-common.xml.i
index 0edc3c37f..23769c8ba 100644
--- a/op-mode-definitions/include/ospf-common.xml.i
+++ b/op-mode-definitions/include/ospf-common.xml.i
@@ -523,15 +523,6 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- <tagNode name="address">
- <properties>
- <help>Show IPv4 OSPF neighbor information for specified IP address</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </tagNode>
<node name="detail">
<properties>
<help>Show detailed IPv4 OSPF neighbor information</help>
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index 352c84ff1..cbdf76fc3 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -14,6 +14,79 @@
</properties>
<command>grc tail --follow=name /var/log/messages</command>
</node>
+ <node name="protocol">
+ <properties>
+ <help>Monitor log for Routing Protocols</help>
+ </properties>
+ <children>
+ <leafNode name="ospf">
+ <properties>
+ <help>Monitor log for OSPF</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/ospfd</command>
+ </leafNode>
+ <leafNode name="ospfv3">
+ <properties>
+ <help>Monitor log for OSPF for IPv6</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/ospf6d</command>
+ </leafNode>
+ <leafNode name="bgp">
+ <properties>
+ <help>Monitor log for BGP</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/bgpd</command>
+ </leafNode>
+ <leafNode name="rip">
+ <properties>
+ <help>Monitor log for RIP</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/ripd</command>
+ </leafNode>
+ <leafNode name="ripng">
+ <properties>
+ <help>Monitor log for RIPng</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/ripngd</command>
+ </leafNode>
+ <leafNode name="static">
+ <properties>
+ <help>Monitor log for static route</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/staticd</command>
+ </leafNode>
+ <leafNode name="multicast">
+ <properties>
+ <help>Monitor log for Multicast protocol</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/pimd</command>
+ </leafNode>
+ <leafNode name="isis">
+ <properties>
+ <help>Monitor log for ISIS</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/isisd</command>
+ </leafNode>
+ <leafNode name="nhrp">
+ <properties>
+ <help>Monitor log for NHRP</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/nhrpd</command>
+ </leafNode>
+ <leafNode name="bfd">
+ <properties>
+ <help>Monitor log for BFD</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/bfdd</command>
+ </leafNode>
+ <leafNode name="mpls">
+ <properties>
+ <help>Monitor log for MPLS</help>
+ </properties>
+ <command>journalctl --follow --boot /usr/lib/frr/ldpd</command>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/op-mode-definitions/policy-route.xml.in b/op-mode-definitions/policy-route.xml.in
index c998e5487..bd4a61dc9 100644
--- a/op-mode-definitions/policy-route.xml.in
+++ b/op-mode-definitions/policy-route.xml.in
@@ -84,17 +84,17 @@
<help>Show policy information</help>
</properties>
<children>
- <node name="ipv6-route">
+ <node name="route6">
<properties>
<help>Show IPv6 policy chain</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/policy_route.py --action show_all --ipv6</command>
</node>
- <tagNode name="ipv6-route">
+ <tagNode name="route6">
<properties>
<help>Show IPv6 policy chains</help>
<completionHelp>
- <path>policy ipv6-route</path>
+ <path>policy route6</path>
</completionHelp>
</properties>
<children>
@@ -102,7 +102,7 @@
<properties>
<help>Show summary of IPv6 policy rules</help>
<completionHelp>
- <path>policy ipv6-route ${COMP_WORDS[4]} rule</path>
+ <path>policy route6 ${COMP_WORDS[4]} rule</path>
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/policy_route.py --action show --name $4 --rule $6 --ipv6</command>
diff --git a/op-mode-definitions/reboot.xml.in b/op-mode-definitions/reboot.xml.in
index 2c8daec5d..6414742d9 100644
--- a/op-mode-definitions/reboot.xml.in
+++ b/op-mode-definitions/reboot.xml.in
@@ -25,7 +25,7 @@
<list>&lt;Minutes&gt;</list>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot $3 $4</command>
+ <command>sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot_in $3 $4</command>
</tagNode>
<tagNode name="at">
<properties>
@@ -40,7 +40,7 @@
<properties>
<help>Reboot at a specific date</help>
<completionHelp>
- <list>&lt;DDMMYYYY&gt; &lt;DD/MM/YYYY&gt; &lt;DD.MM.YYYY&gt; &lt;DD:MM:YYYY&gt;</list>
+ <list>&lt;DD/MM/YYYY&gt; &lt;DD.MM.YYYY&gt; &lt;DD:MM:YYYY&gt;</list>
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot $3 $5</command>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 4c0a7913b..15bbc7f42 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -212,6 +212,79 @@
</tagNode>
</children>
</node>
+ <node name="protocol">
+ <properties>
+ <help>Show log for Routing Protocols</help>
+ </properties>
+ <children>
+ <leafNode name="ospf">
+ <properties>
+ <help>Show log for OSPF</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/ospfd</command>
+ </leafNode>
+ <leafNode name="ospfv3">
+ <properties>
+ <help>Show log for OSPF for IPv6</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/ospf6d</command>
+ </leafNode>
+ <leafNode name="bgp">
+ <properties>
+ <help>Show log for BGP</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/bgpd</command>
+ </leafNode>
+ <leafNode name="rip">
+ <properties>
+ <help>Show log for RIP</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/ripd</command>
+ </leafNode>
+ <leafNode name="ripng">
+ <properties>
+ <help>Show log for RIPng</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/ripngd</command>
+ </leafNode>
+ <leafNode name="static">
+ <properties>
+ <help>Show log for static route</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/staticd</command>
+ </leafNode>
+ <leafNode name="multicast">
+ <properties>
+ <help>Show log for Multicast protocol</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/pimd</command>
+ </leafNode>
+ <leafNode name="isis">
+ <properties>
+ <help>Show log for ISIS</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/isisd</command>
+ </leafNode>
+ <leafNode name="nhrp">
+ <properties>
+ <help>Show log for NHRP</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/nhrpd</command>
+ </leafNode>
+ <leafNode name="bfd">
+ <properties>
+ <help>Show log for BFD</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/bfdd</command>
+ </leafNode>
+ <leafNode name="mpls">
+ <properties>
+ <help>Show log for MPLS</help>
+ </properties>
+ <command>journalctl --boot /usr/lib/frr/ldpd</command>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="snmp">
<properties>
<help>Show log for Simple Network Monitoring Protocol (SNMP)</help>
diff --git a/op-mode-definitions/show-virtual-server.xml.in b/op-mode-definitions/show-virtual-server.xml.in
new file mode 100644
index 000000000..5dbd3c759
--- /dev/null
+++ b/op-mode-definitions/show-virtual-server.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="virtual-server">
+ <properties>
+ <help>Show virtual server information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_virtual_server.py</command>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/base.py b/python/vyos/base.py
index c78045548..fd22eaccd 100644
--- a/python/vyos/base.py
+++ b/python/vyos/base.py
@@ -15,6 +15,12 @@
from textwrap import fill
+class DeprecationWarning():
+ def __init__(self, message):
+ # Reformat the message and trim it to 72 characters in length
+ message = fill(message, width=72)
+ print(f'\nDEPRECATION WARNING: {message}\n')
+
class ConfigError(Exception):
def __init__(self, message):
# Reformat the message and trim it to 72 characters in length
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index d974a7565..551c27b67 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -117,6 +117,12 @@ def leaf_node_changed(conf, path):
D.set_level(conf.get_level())
(new, old) = D.get_value_diff(path)
if new != old:
+ if isinstance(old, dict):
+ # valueLess nodes return {} if node is deleted
+ return True
+ if old is None and isinstance(new, dict):
+ # valueLess nodes return {} if node was added
+ return True
if old is None:
return []
if isinstance(old, str):
@@ -130,7 +136,7 @@ def leaf_node_changed(conf, path):
return None
-def node_changed(conf, path, key_mangling=None):
+def node_changed(conf, path, key_mangling=None, recursive=False):
"""
Check if a leaf node was altered. If it has been altered - values has been
changed, or it was added/removed, we will return the old value. If nothing
@@ -140,7 +146,7 @@ def node_changed(conf, path, key_mangling=None):
D = get_config_diff(conf, key_mangling)
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(path, expand_nodes=Diff.DELETE)['delete'].keys()
+ keys = D.get_child_nodes_diff(path, expand_nodes=Diff.DELETE, recursive=recursive)['delete'].keys()
return list(keys)
def get_removed_vlans(conf, dict):
@@ -196,7 +202,7 @@ def is_member(conf, interface, intftype=None):
interface name -> Interface is a member of this interface
False -> interface type cannot have members
"""
- ret_val = None
+ ret_val = {}
intftypes = ['bonding', 'bridge']
if intftype not in intftypes + [None]:
@@ -216,8 +222,8 @@ def is_member(conf, interface, intftype=None):
member = base + [intf, 'member', 'interface', interface]
if conf.exists(member):
tmp = conf.get_config_dict(member, key_mangling=('-', '_'),
- get_first_key=True)
- ret_val = {intf : tmp}
+ get_first_key=True, no_tag_node_value_mangle=True)
+ ret_val.update({intf : tmp})
old_level = conf.set_level(old_level)
return ret_val
@@ -319,34 +325,42 @@ def is_source_interface(conf, interface, intftype=None):
def get_dhcp_interfaces(conf, vrf=None):
""" Common helper functions to retrieve all interfaces from current CLI
sessions that have DHCP configured. """
- dhcp_interfaces = []
+ dhcp_interfaces = {}
dict = conf.get_config_dict(['interfaces'], get_first_key=True)
if not dict:
return dhcp_interfaces
def check_dhcp(config, ifname):
- out = []
+ tmp = {}
if 'address' in config and 'dhcp' in config['address']:
+ options = {}
+ if 'dhcp_options' in config and 'default_route_distance' in config['dhcp_options']:
+ options.update({'distance' : config['dhcp_options']['default_route_distance']})
if 'vrf' in config:
- if vrf is config['vrf']: out.append(ifname)
- else: out.append(ifname)
- return out
+ if vrf is config['vrf']: tmp.update({ifname : options})
+ else: tmp.update({ifname : options})
+ return tmp
for section, interface in dict.items():
- for ifname, ifconfig in interface.items():
+ for ifname in interface:
+ # we already have a dict representation of the config from get_config_dict(),
+ # but with the extended information from get_interface_dict() we also
+ # get the DHCP client default-route-distance default option if not specified.
+ ifconfig = get_interface_dict(conf, ['interfaces', section], ifname)
+
tmp = check_dhcp(ifconfig, ifname)
- dhcp_interfaces.extend(tmp)
+ dhcp_interfaces.update(tmp)
# check per VLAN interfaces
for vif, vif_config in ifconfig.get('vif', {}).items():
tmp = check_dhcp(vif_config, f'{ifname}.{vif}')
- dhcp_interfaces.extend(tmp)
+ dhcp_interfaces.update(tmp)
# check QinQ VLAN interfaces
for vif_s, vif_s_config in ifconfig.get('vif-s', {}).items():
tmp = check_dhcp(vif_s_config, f'{ifname}.{vif_s}')
- dhcp_interfaces.extend(tmp)
+ dhcp_interfaces.update(tmp)
for vif_c, vif_c_config in vif_s_config.get('vif-c', {}).items():
tmp = check_dhcp(vif_c_config, f'{ifname}.{vif_s}.{vif_c}')
- dhcp_interfaces.extend(tmp)
+ dhcp_interfaces.update(tmp)
return dhcp_interfaces
@@ -405,6 +419,12 @@ def get_interface_dict(config, base, ifname=''):
if 'deleted' not in dict:
dict = dict_merge(default_values, dict)
+ # If interface does not request an IPv4 DHCP address there is no need
+ # to keep the dhcp-options key
+ if 'address' not in dict or 'dhcp' not in dict['address']:
+ if 'dhcp_options' in dict:
+ del dict['dhcp_options']
+
# XXX: T2665: blend in proper DHCPv6-PD default values
dict = T2665_set_dhcpv6pd_defaults(dict)
@@ -423,6 +443,10 @@ def get_interface_dict(config, base, ifname=''):
bond = is_member(config, ifname, 'bonding')
if bond: dict.update({'is_bond_member' : bond})
+ # Check if any DHCP options changed which require a client restat
+ dhcp = node_changed(config, ['dhcp-options'], recursive=True)
+ if dhcp: dict.update({'dhcp_options_changed' : ''})
+
# Some interfaces come with a source_interface which must also not be part
# of any other bond or bridge interface as it is exclusivly assigned as the
# Kernels "lower" interface to this new "virtual/upper" interface.
@@ -466,10 +490,20 @@ def get_interface_dict(config, base, ifname=''):
# XXX: T2665: blend in proper DHCPv6-PD default values
dict['vif'][vif] = T2665_set_dhcpv6pd_defaults(dict['vif'][vif])
+ # If interface does not request an IPv4 DHCP address there is no need
+ # to keep the dhcp-options key
+ if 'address' not in dict['vif'][vif] or 'dhcp' not in dict['vif'][vif]['address']:
+ if 'dhcp_options' in dict['vif'][vif]:
+ del dict['vif'][vif]['dhcp_options']
+
# Check if we are a member of a bridge device
bridge = is_member(config, f'{ifname}.{vif}', 'bridge')
if bridge: dict['vif'][vif].update({'is_bridge_member' : bridge})
+ # Check if any DHCP options changed which require a client restat
+ dhcp = node_changed(config, ['vif', vif, 'dhcp-options'], recursive=True)
+ if dhcp: dict['vif'][vif].update({'dhcp_options_changed' : ''})
+
for vif_s, vif_s_config in dict.get('vif_s', {}).items():
default_vif_s_values = defaults(base + ['vif-s'])
# XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c
@@ -491,10 +525,21 @@ def get_interface_dict(config, base, ifname=''):
# XXX: T2665: blend in proper DHCPv6-PD default values
dict['vif_s'][vif_s] = T2665_set_dhcpv6pd_defaults(dict['vif_s'][vif_s])
+ # If interface does not request an IPv4 DHCP address there is no need
+ # to keep the dhcp-options key
+ if 'address' not in dict['vif_s'][vif_s] or 'dhcp' not in \
+ dict['vif_s'][vif_s]['address']:
+ if 'dhcp_options' in dict['vif_s'][vif_s]:
+ del dict['vif_s'][vif_s]['dhcp_options']
+
# Check if we are a member of a bridge device
bridge = is_member(config, f'{ifname}.{vif_s}', 'bridge')
if bridge: dict['vif_s'][vif_s].update({'is_bridge_member' : bridge})
+ # Check if any DHCP options changed which require a client restat
+ dhcp = node_changed(config, ['vif-s', vif_s, 'dhcp-options'], recursive=True)
+ if dhcp: dict['vif_s'][vif_s].update({'dhcp_options_changed' : ''})
+
for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items():
default_vif_c_values = defaults(base + ['vif-s', 'vif-c'])
@@ -516,11 +561,22 @@ def get_interface_dict(config, base, ifname=''):
dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_set_dhcpv6pd_defaults(
dict['vif_s'][vif_s]['vif_c'][vif_c])
+ # If interface does not request an IPv4 DHCP address there is no need
+ # to keep the dhcp-options key
+ if 'address' not in dict['vif_s'][vif_s]['vif_c'][vif_c] or 'dhcp' \
+ not in dict['vif_s'][vif_s]['vif_c'][vif_c]['address']:
+ if 'dhcp_options' in dict['vif_s'][vif_s]['vif_c'][vif_c]:
+ del dict['vif_s'][vif_s]['vif_c'][vif_c]['dhcp_options']
+
# Check if we are a member of a bridge device
bridge = is_member(config, f'{ifname}.{vif_s}.{vif_c}', 'bridge')
if bridge: dict['vif_s'][vif_s]['vif_c'][vif_c].update(
{'is_bridge_member' : bridge})
+ # Check if any DHCP options changed which require a client restat
+ dhcp = node_changed(config, ['vif-s', vif_s, 'vif-c', vif_c, 'dhcp-options'], recursive=True)
+ if dhcp: dict['vif_s'][vif_s]['vif_c'][vif_c].update({'dhcp_options_changed' : ''})
+
# Check vif, vif-s/vif-c VLAN interfaces for removal
dict = get_removed_vlans(config, dict)
return dict
@@ -545,7 +601,6 @@ def get_vlan_ids(interface):
return vlan_ids
-
def get_accel_dict(config, base, chap_secrets):
"""
Common utility function to retrieve and mangle the Accel-PPP configuration
diff --git a/python/vyos/configdiff.py b/python/vyos/configdiff.py
index 4ad7443d7..9185575df 100644
--- a/python/vyos/configdiff.py
+++ b/python/vyos/configdiff.py
@@ -16,6 +16,7 @@
from enum import IntFlag, auto
from vyos.config import Config
+from vyos.configtree import DiffTree
from vyos.configdict import dict_merge
from vyos.configdict import list_diff
from vyos.util import get_sub_dict, mangle_dict_keys
@@ -38,6 +39,8 @@ class Diff(IntFlag):
ADD = auto()
STABLE = auto()
+ALL = Diff.MERGE | Diff.DELETE | Diff.ADD | Diff.STABLE
+
requires_effective = [enum_to_key(Diff.DELETE)]
target_defaults = [enum_to_key(Diff.MERGE)]
@@ -75,19 +78,24 @@ def get_config_diff(config, key_mangling=None):
isinstance(key_mangling[1], str)):
raise ValueError("key_mangling must be a tuple of two strings")
- return ConfigDiff(config, key_mangling)
+ diff_t = DiffTree(config._running_config, config._session_config)
+
+ return ConfigDiff(config, key_mangling, diff_tree=diff_t)
class ConfigDiff(object):
"""
The class of config changes as represented by comparison between the
session config dict and the effective config dict.
"""
- def __init__(self, config, key_mangling=None):
+ def __init__(self, config, key_mangling=None, diff_tree=None):
self._level = config.get_level()
self._session_config_dict = config.get_cached_root_dict(effective=False)
self._effective_config_dict = config.get_cached_root_dict(effective=True)
self._key_mangling = key_mangling
+ self._diff_tree = diff_tree
+ self._diff_dict = diff_tree.dict if diff_tree else {}
+
# mirrored from Config; allow path arguments relative to level
def _make_path(self, path):
if isinstance(path, str):
@@ -136,6 +144,15 @@ class ConfigDiff(object):
self._key_mangling[1])
return config_dict
+ def is_node_changed(self, path=[]):
+ if self._diff_tree is None:
+ raise NotImplementedError("diff_tree class not available")
+
+ if (self._diff_tree.add.exists(self._make_path(path)) or
+ self._diff_tree.sub.exists(self._make_path(path))):
+ return True
+ return False
+
def get_child_nodes_diff_str(self, path=[]):
ret = {'add': {}, 'change': {}, 'delete': {}}
@@ -164,7 +181,8 @@ class ConfigDiff(object):
return ret
- def get_child_nodes_diff(self, path=[], expand_nodes=Diff(0), no_defaults=False):
+ def get_child_nodes_diff(self, path=[], expand_nodes=Diff(0), no_defaults=False,
+ recursive=False):
"""
Args:
path (str|list): config path
@@ -174,6 +192,8 @@ class ConfigDiff(object):
value
no_detaults=False: if expand_nodes & Diff.MERGE, do not merge default
values to ret['merge']
+ recursive: if true, use config_tree diff algorithm provided by
+ diff_tree class
Returns: dict of lists, representing differences between session
and effective config, under path
@@ -184,6 +204,34 @@ class ConfigDiff(object):
"""
session_dict = get_sub_dict(self._session_config_dict,
self._make_path(path), get_first_key=True)
+
+ if recursive:
+ if self._diff_tree is None:
+ raise NotImplementedError("diff_tree class not available")
+ else:
+ add = get_sub_dict(self._diff_tree.dict, ['add'], get_first_key=True)
+ sub = get_sub_dict(self._diff_tree.dict, ['sub'], get_first_key=True)
+ inter = get_sub_dict(self._diff_tree.dict, ['inter'], get_first_key=True)
+ ret = {}
+ ret[enum_to_key(Diff.MERGE)] = session_dict
+ ret[enum_to_key(Diff.DELETE)] = get_sub_dict(sub, self._make_path(path),
+ get_first_key=True)
+ ret[enum_to_key(Diff.ADD)] = get_sub_dict(add, self._make_path(path),
+ get_first_key=True)
+ ret[enum_to_key(Diff.STABLE)] = get_sub_dict(inter, self._make_path(path),
+ get_first_key=True)
+ for e in Diff:
+ k = enum_to_key(e)
+ if not (e & expand_nodes):
+ ret[k] = list(ret[k])
+ else:
+ if self._key_mangling:
+ ret[k] = self._mangle_dict_keys(ret[k])
+ if k in target_defaults and not no_defaults:
+ default_values = defaults(self._make_path(path))
+ ret[k] = dict_merge(default_values, ret[k])
+ return ret
+
effective_dict = get_sub_dict(self._effective_config_dict,
self._make_path(path), get_first_key=True)
@@ -209,7 +257,8 @@ class ConfigDiff(object):
return ret
- def get_node_diff(self, path=[], expand_nodes=Diff(0), no_defaults=False):
+ def get_node_diff(self, path=[], expand_nodes=Diff(0), no_defaults=False,
+ recursive=False):
"""
Args:
path (str|list): config path
@@ -219,6 +268,8 @@ class ConfigDiff(object):
value
no_detaults=False: if expand_nodes & Diff.MERGE, do not merge default
values to ret['merge']
+ recursive: if true, use config_tree diff algorithm provided by
+ diff_tree class
Returns: dict of lists, representing differences between session
and effective config, at path
@@ -228,6 +279,31 @@ class ConfigDiff(object):
dict['stable'] = config values in both session and effective
"""
session_dict = get_sub_dict(self._session_config_dict, self._make_path(path))
+
+ if recursive:
+ if self._diff_tree is None:
+ raise NotImplementedError("diff_tree class not available")
+ else:
+ add = get_sub_dict(self._diff_tree.dict, ['add'], get_first_key=True)
+ sub = get_sub_dict(self._diff_tree.dict, ['sub'], get_first_key=True)
+ inter = get_sub_dict(self._diff_tree.dict, ['inter'], get_first_key=True)
+ ret = {}
+ ret[enum_to_key(Diff.MERGE)] = session_dict
+ ret[enum_to_key(Diff.DELETE)] = get_sub_dict(sub, self._make_path(path))
+ ret[enum_to_key(Diff.ADD)] = get_sub_dict(add, self._make_path(path))
+ ret[enum_to_key(Diff.STABLE)] = get_sub_dict(inter, self._make_path(path))
+ for e in Diff:
+ k = enum_to_key(e)
+ if not (e & expand_nodes):
+ ret[k] = list(ret[k])
+ else:
+ if self._key_mangling:
+ ret[k] = self._mangle_dict_keys(ret[k])
+ if k in target_defaults and not no_defaults:
+ default_values = defaults(self._make_path(path))
+ ret[k] = dict_merge(default_values, ret[k])
+ return ret
+
effective_dict = get_sub_dict(self._effective_config_dict, self._make_path(path))
ret = _key_sets_from_dicts(session_dict, effective_dict)
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py
index d8ffaca99..e9cdb69e4 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -17,6 +17,7 @@ import json
from ctypes import cdll, c_char_p, c_void_p, c_int
+LIBPATH = '/usr/lib/libvyosconfig.so.0'
def escape_backslash(string: str) -> str:
"""Escape single backslashes in string that are not in escape sequence"""
@@ -42,7 +43,9 @@ class ConfigTreeError(Exception):
class ConfigTree(object):
- def __init__(self, config_string, libpath='/usr/lib/libvyosconfig.so.0'):
+ def __init__(self, config_string=None, address=None, libpath=LIBPATH):
+ if config_string is None and address is None:
+ raise TypeError("ConfigTree() requires one of 'config_string' or 'address'")
self.__config = None
self.__lib = cdll.LoadLibrary(libpath)
@@ -60,7 +63,7 @@ class ConfigTree(object):
self.__to_string.restype = c_char_p
self.__to_commands = self.__lib.to_commands
- self.__to_commands.argtypes = [c_void_p]
+ self.__to_commands.argtypes = [c_void_p, c_char_p]
self.__to_commands.restype = c_char_p
self.__to_json = self.__lib.to_json
@@ -123,18 +126,26 @@ class ConfigTree(object):
self.__set_tag.argtypes = [c_void_p, c_char_p]
self.__set_tag.restype = c_int
+ self.__get_subtree = self.__lib.get_subtree
+ self.__get_subtree.argtypes = [c_void_p, c_char_p]
+ self.__get_subtree.restype = c_void_p
+
self.__destroy = self.__lib.destroy
self.__destroy.argtypes = [c_void_p]
- config_section, version_section = extract_version(config_string)
- config_section = escape_backslash(config_section)
- config = self.__from_string(config_section.encode())
- if config is None:
- msg = self.__get_error().decode()
- raise ValueError("Failed to parse config: {0}".format(msg))
+ if address is None:
+ config_section, version_section = extract_version(config_string)
+ config_section = escape_backslash(config_section)
+ config = self.__from_string(config_section.encode())
+ if config is None:
+ msg = self.__get_error().decode()
+ raise ValueError("Failed to parse config: {0}".format(msg))
+ else:
+ self.__config = config
+ self.__version = version_section
else:
- self.__config = config
- self.__version = version_section
+ self.__config = address
+ self.__version = ''
def __del__(self):
if self.__config is not None:
@@ -143,13 +154,16 @@ class ConfigTree(object):
def __str__(self):
return self.to_string()
+ def _get_config(self):
+ return self.__config
+
def to_string(self):
config_string = self.__to_string(self.__config).decode()
config_string = "{0}\n{1}".format(config_string, self.__version)
return config_string
- def to_commands(self):
- return self.__to_commands(self.__config).decode()
+ def to_commands(self, op="set"):
+ return self.__to_commands(self.__config, op.encode()).decode()
def to_json(self):
return self.__to_json(self.__config).decode()
@@ -281,3 +295,61 @@ class ConfigTree(object):
else:
raise ConfigTreeError("Path [{}] doesn't exist".format(path_str))
+ def get_subtree(self, path, with_node=False):
+ check_path(path)
+ path_str = " ".join(map(str, path)).encode()
+
+ res = self.__get_subtree(self.__config, path_str, with_node)
+ subt = ConfigTree(address=res)
+ return subt
+
+class DiffTree:
+ def __init__(self, left, right, path=[], libpath=LIBPATH):
+ if left is None:
+ left = ConfigTree(config_string='\n')
+ if right is None:
+ right = ConfigTree(config_string='\n')
+ if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)):
+ raise TypeError("Arguments must be instances of ConfigTree")
+ if path:
+ if not left.exists(path):
+ raise ConfigTreeError(f"Path {path} doesn't exist in lhs tree")
+ if not right.exists(path):
+ raise ConfigTreeError(f"Path {path} doesn't exist in rhs tree")
+
+ self.left = left
+ self.right = right
+
+ self.__lib = cdll.LoadLibrary(libpath)
+
+ self.__diff_tree = self.__lib.diff_tree
+ self.__diff_tree.argtypes = [c_char_p, c_void_p, c_void_p]
+ self.__diff_tree.restype = c_void_p
+
+ self.__trim_tree = self.__lib.trim_tree
+ self.__trim_tree.argtypes = [c_void_p, c_void_p]
+ self.__trim_tree.restype = c_void_p
+
+ check_path(path)
+ path_str = " ".join(map(str, path)).encode()
+
+ res = self.__diff_tree(path_str, left._get_config(), right._get_config())
+
+ # full diff config_tree and python dict representation
+ self.full = ConfigTree(address=res)
+ self.dict = json.loads(self.full.to_json())
+
+ # config_tree sub-trees
+ self.add = self.full.get_subtree(['add'])
+ self.sub = self.full.get_subtree(['sub'])
+ self.inter = self.full.get_subtree(['inter'])
+
+ # trim sub(-tract) tree to get delete tree for commands
+ ref = self.right.get_subtree(path, with_node=True) if path else self.right
+ res = self.__trim_tree(self.sub._get_config(), ref._get_config())
+ self.delete = ConfigTree(address=res)
+
+ def to_commands(self):
+ add = self.add.to_commands()
+ delete = self.delete.to_commands(op="delete")
+ return delete + "\n" + add
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 365a28feb..1062d51ee 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -173,24 +173,43 @@ def verify_eapol(config):
if ca_cert_name not in config['pki']['ca']:
raise ConfigError('Invalid CA certificate specified for EAPoL')
- ca_cert = config['pki']['ca'][cert_name]
+ ca_cert = config['pki']['ca'][ca_cert_name]
if 'certificate' not in ca_cert:
raise ConfigError('Invalid CA certificate specified for EAPoL')
-def verify_mirror(config):
+def verify_mirror_redirect(config):
"""
Common helper function used by interface implementations to perform
- recurring validation of mirror interface configuration.
+ recurring validation of mirror and redirect interface configuration via tc(8)
It makes no sense to mirror traffic back at yourself!
"""
+ import os
+ if {'mirror', 'redirect'} <= set(config):
+ raise ConfigError('Mirror and redirect can not be enabled at the same time!')
+
if 'mirror' in config:
for direction, mirror_interface in config['mirror'].items():
+ if not os.path.exists(f'/sys/class/net/{mirror_interface}'):
+ raise ConfigError(f'Requested mirror interface "{mirror_interface}" '\
+ 'does not exist!')
+
if mirror_interface == config['ifname']:
- raise ConfigError(f'Can not mirror "{direction}" traffic back ' \
+ raise ConfigError(f'Can not mirror "{direction}" traffic back '\
'the originating interface!')
+ if 'redirect' in config:
+ redirect_ifname = config['redirect']
+ if not os.path.exists(f'/sys/class/net/{redirect_ifname}'):
+ raise ConfigError(f'Requested redirect interface "{redirect_ifname}" '\
+ 'does not exist!')
+
+ if dict_search('traffic_policy.in', config) != None:
+ # XXX: support combination of limiting and redirect/mirror - this is an
+ # artificial limitation
+ raise ConfigError('Can not use ingress policy tigether with mirror or redirect!')
+
def verify_authentication(config):
"""
Common helper function used by interface implementations to perform
@@ -224,9 +243,10 @@ def verify_bridge_delete(config):
when interface also is part of a bridge.
"""
if 'is_bridge_member' in config:
- raise ConfigError(
- 'Interface "{ifname}" cannot be deleted as it is a '
- 'member of bridge "{is_bridge_member}"!'.format(**config))
+ interface = config['ifname']
+ for bridge in config['is_bridge_member']:
+ raise ConfigError(f'Interface "{interface}" cannot be deleted as it '
+ f'is a member of bridge "{bridge}"!')
def verify_interface_exists(ifname):
"""
@@ -308,27 +328,37 @@ def verify_vlan_config(config):
if duplicate:
raise ConfigError(f'Duplicate VLAN id "{duplicate[0]}" used for vif and vif-s interfaces!')
+ parent_ifname = config['ifname']
# 802.1q VLANs
- for vlan in config.get('vif', {}):
- vlan = config['vif'][vlan]
+ for vlan_id in config.get('vif', {}):
+ vlan = config['vif'][vlan_id]
+ vlan['ifname'] = f'{parent_ifname}.{vlan_id}'
+
verify_dhcpv6(vlan)
verify_address(vlan)
verify_vrf(vlan)
+ verify_mirror_redirect(vlan)
verify_mtu_parent(vlan, config)
# 802.1ad (Q-in-Q) VLANs
- for s_vlan in config.get('vif_s', {}):
- s_vlan = config['vif_s'][s_vlan]
+ for s_vlan_id in config.get('vif_s', {}):
+ s_vlan = config['vif_s'][s_vlan_id]
+ s_vlan['ifname'] = f'{parent_ifname}.{s_vlan_id}'
+
verify_dhcpv6(s_vlan)
verify_address(s_vlan)
verify_vrf(s_vlan)
+ verify_mirror_redirect(s_vlan)
verify_mtu_parent(s_vlan, config)
- for c_vlan in s_vlan.get('vif_c', {}):
- c_vlan = s_vlan['vif_c'][c_vlan]
+ for c_vlan_id in s_vlan.get('vif_c', {}):
+ c_vlan = s_vlan['vif_c'][c_vlan_id]
+ c_vlan['ifname'] = f'{parent_ifname}.{s_vlan_id}.{c_vlan_id}'
+
verify_dhcpv6(c_vlan)
verify_address(c_vlan)
verify_vrf(c_vlan)
+ verify_mirror_redirect(c_vlan)
verify_mtu_parent(c_vlan, config)
verify_mtu_parent(c_vlan, s_vlan)
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index c77b695bd..fcb6a7fbc 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -48,6 +48,7 @@ api_data = {
'port' : '8080',
'socket' : False,
'strict' : False,
+ 'gql' : False,
'debug' : False,
'api_keys' : [ {"id": "testapp", "key": "qwerty"} ]
}
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index e45b0f041..672ee2cc9 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -18,6 +18,9 @@ import re
from vyos.util import popen
+# These drivers do not support using ethtool to change the speed, duplex, or flow control settings
+_drivers_without_speed_duplex_flow = ['vmxnet3', 'virtio_net', 'xen_netfront', 'iavf', 'ice', 'i40e']
+
class Ethtool:
"""
Class is used to retrive and cache information about an ethernet adapter
@@ -41,7 +44,7 @@ class Ethtool:
# '100' : {'full': '', 'half': ''},
# '1000': {'full': ''}
# }
- _speed_duplex = { }
+ _speed_duplex = {'auto': {'auto': ''}}
_ring_buffers = { }
_ring_buffers_max = { }
_driver_name = None
@@ -188,7 +191,7 @@ class Ethtool:
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']:
+ if self.get_driver_name() in _drivers_without_speed_duplex_flow:
return False
if speed in self._speed_duplex:
@@ -198,7 +201,7 @@ class Ethtool:
def check_flow_control(self):
""" Check if the NIC supports flow-control """
- if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']:
+ if self.get_driver_name() in _drivers_without_speed_duplex_flow:
return False
return self._flow_control
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 8b7402b7e..ff8623592 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -45,13 +45,19 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'state' in rule_conf and rule_conf['state']:
states = ",".join([s for s, v in rule_conf['state'].items() if v == 'enable'])
- output.append(f'ct state {{{states}}}')
+
+ if states:
+ output.append(f'ct state {{{states}}}')
if 'protocol' in rule_conf and rule_conf['protocol'] != 'all':
proto = rule_conf['protocol']
+ operator = ''
+ if proto[0] == '!':
+ operator = '!='
+ proto = proto[1:]
if proto == 'tcp_udp':
proto = '{tcp, udp}'
- output.append('meta l4proto ' + proto)
+ output.append(f'meta l4proto {operator} {proto}')
for side in ['destination', 'source']:
if side in rule_conf:
@@ -59,7 +65,10 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
side_conf = rule_conf[side]
if 'address' in side_conf:
- output.append(f'{ip_name} {prefix}addr {side_conf["address"]}')
+ suffix = side_conf['address']
+ if suffix[0] == '!':
+ suffix = f'!= {suffix[1:]}'
+ output.append(f'{ip_name} {prefix}addr {suffix}')
if 'mac_address' in side_conf:
suffix = side_conf["mac_address"]
@@ -69,24 +78,51 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'port' in side_conf:
proto = rule_conf['protocol']
- port = side_conf["port"]
+ port = side_conf['port'].split(',')
+
+ ports = []
+ negated_ports = []
- if isinstance(port, list):
- port = ",".join(port)
+ for p in port:
+ if p[0] == '!':
+ negated_ports.append(p[1:])
+ else:
+ ports.append(p)
if proto == 'tcp_udp':
proto = 'th'
- output.append(f'{proto} {prefix}port {{{port}}}')
+ if ports:
+ ports_str = ','.join(ports)
+ output.append(f'{proto} {prefix}port {{{ports_str}}}')
+
+ if negated_ports:
+ negated_ports_str = ','.join(negated_ports)
+ output.append(f'{proto} {prefix}port != {{{negated_ports_str}}}')
if 'group' in side_conf:
group = side_conf['group']
if 'address_group' in group:
group_name = group['address_group']
- output.append(f'{ip_name} {prefix}addr $A{def_suffix}_{group_name}')
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+ output.append(f'{ip_name} {prefix}addr {operator} $A{def_suffix}_{group_name}')
elif 'network_group' in group:
group_name = group['network_group']
- output.append(f'{ip_name} {prefix}addr $N{def_suffix}_{group_name}')
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+ output.append(f'{ip_name} {prefix}addr {operator} $N{def_suffix}_{group_name}')
+ if 'mac_group' in group:
+ group_name = group['mac_group']
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+ output.append(f'ether {prefix}addr {operator} $M_{group_name}')
if 'port_group' in group:
proto = rule_conf['protocol']
group_name = group['port_group']
@@ -94,10 +130,16 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if proto == 'tcp_udp':
proto = 'th'
- output.append(f'{proto} {prefix}port $P_{group_name}')
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+
+ output.append(f'{proto} {prefix}port {operator} $P_{group_name}')
if 'log' in rule_conf and rule_conf['log'] == 'enable':
- output.append('log')
+ action = rule_conf['action'] if 'action' in rule_conf else 'accept'
+ output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}] "')
if 'hop_limit' in rule_conf:
operators = {'eq': '==', 'gt': '>', 'lt': '<'}
@@ -132,16 +174,14 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'limit' in rule_conf:
if 'rate' in rule_conf['limit']:
- output.append(f'limit rate {rule_conf["limit"]["rate"]}/second')
+ output.append(f'limit rate {rule_conf["limit"]["rate"]}')
if 'burst' in rule_conf['limit']:
output.append(f'burst {rule_conf["limit"]["burst"]} packets')
if 'recent' in rule_conf:
count = rule_conf['recent']['count']
time = rule_conf['recent']['time']
- # output.append(f'meter {fw_name}_{rule_id} {{ ip saddr and 255.255.255.255 limit rate over {count}/{time} burst {count} packets }}')
- # Waiting on input from nftables developers due to
- # bug with above line and atomic chain flushing.
+ output.append(f'add @RECENT{def_suffix}_{fw_name}_{rule_id} {{ {ip_name} saddr limit rate over {count}/{time} burst {count} packets }}')
if 'time' in rule_conf:
output.append(parse_time(rule_conf['time']))
@@ -150,7 +190,6 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if tcp_flags:
output.append(parse_tcp_flags(tcp_flags))
-
output.append('counter')
if 'set' in rule_conf:
@@ -165,15 +204,9 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
return " ".join(output)
def parse_tcp_flags(flags):
- all_flags = []
- include = []
- for flag in flags.split(","):
- if flag[0] == '!':
- flag = flag[1:]
- else:
- include.append(flag)
- all_flags.append(flag)
- return f'tcp flags & ({"|".join(all_flags)}) == {"|".join(include)}'
+ include = [flag for flag in flags if flag != 'not']
+ exclude = list(flags['not']) if 'not' in flags else []
+ return f'tcp flags & ({"|".join(include + exclude)}) == {"|".join(include) if include else "0x0"}'
def parse_time(time):
out = []
@@ -209,7 +242,7 @@ def parse_policy_set(set_conf, def_suffix):
table = set_conf['table']
if table == 'main':
table = '254'
- mark = 0x7FFFFFFF - int(set_conf['table'])
+ mark = 0x7FFFFFFF - int(table)
out.append(f'meta mark set {mark}')
if 'tcp_mss' in set_conf:
mss = set_conf['tcp_mss']
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index a8f115d9a..cbba19ab7 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -73,15 +73,15 @@ from vyos.util import cmd
import logging
from logging.handlers import SysLogHandler
import os
+import sys
+
LOG = logging.getLogger(__name__)
+DEBUG = False
-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)
+ch = SysLogHandler(address='/dev/log')
+ch2 = logging.StreamHandler(stream=sys.stdout)
+LOG.addHandler(ch)
+LOG.addHandler(ch2)
_frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd',
'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd',
@@ -121,6 +121,12 @@ class ConfigSectionNotFound(FrrError):
"""
pass
+def init_debugging():
+ global DEBUG
+
+ DEBUG = os.path.exists('/tmp/vyos.frr.debug')
+ if DEBUG:
+ LOG.setLevel(logging.DEBUG)
def get_configuration(daemon=None, marked=False):
""" Get current running FRR configuration
@@ -424,6 +430,8 @@ class FRRConfig:
Using this overwrites the current loaded config objects and replaces the original loaded config
'''
+ init_debugging()
+
self.imported_config = get_configuration(daemon=daemon)
if daemon:
LOG.debug(f'load_configuration: Configuration loaded from FRR daemon {daemon}')
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 27073b266..ffd9c590f 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -298,7 +298,6 @@ class BridgeIf(Interface):
tmp = dict_search('member.interface', config)
if tmp:
-
for interface, interface_config in tmp.items():
# if interface does yet not exist bail out early and
# add it later
@@ -316,10 +315,13 @@ class BridgeIf(Interface):
# 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)
+ if not interface.startswith('wlan'):
+ # always set private-vlan/port isolation - this can not be
+ # done when lower link is a wifi link, as it will trigger:
+ # RTNETLINK answers: Operation not supported
+ 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:
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 91c7f0c33..6b0f08fd4 100755
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -13,7 +13,6 @@
# 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/>.
-from netifaces import interfaces
import os
import re
import json
@@ -1080,16 +1079,17 @@ class Interface(Control):
if addr in self._addr:
return False
- addr_is_v4 = is_ipv4(addr)
-
# add to interface
if addr == 'dhcp':
self.set_dhcp(True)
elif addr == 'dhcpv6':
self.set_dhcpv6(True)
elif not is_intf_addr_assigned(self.ifname, addr):
- self._cmd(f'ip addr add "{addr}" '
- f'{"brd + " if addr_is_v4 else ""}dev "{self.ifname}"')
+ tmp = f'ip addr add {addr} dev {self.ifname}'
+ # Add broadcast address for IPv4
+ if is_ipv4(addr): tmp += ' brd +'
+
+ self._cmd(tmp)
else:
return False
@@ -1228,12 +1228,11 @@ class Interface(Control):
options_file = f'{config_base}_{ifname}.options'
pid_file = f'{config_base}_{ifname}.pid'
lease_file = f'{config_base}_{ifname}.leases'
-
- # Stop client with old config files to get the right IF_METRIC.
systemd_service = f'dhclient@{ifname}.service'
- if is_systemd_service_active(systemd_service):
- self._cmd(f'systemctl stop {systemd_service}')
+ # '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.
if enable and 'disable' not in self._config:
if dict_search('dhcp_options.host_name', self._config) == None:
# read configured system hostname.
@@ -1244,16 +1243,19 @@ class Interface(Control):
tmp = {'dhcp_options' : { 'host_name' : hostname}}
self._config = dict_merge(tmp, self._config)
- render(options_file, 'dhcp-client/daemon-options.tmpl',
- self._config)
- render(config_file, 'dhcp-client/ipv4.tmpl',
- self._config)
+ render(options_file, 'dhcp-client/daemon-options.tmpl', self._config)
+ render(config_file, 'dhcp-client/ipv4.tmpl', self._config)
- # '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 restart {systemd_service}')
+ # When the DHCP client is restarted a brief outage will occur, as
+ # the old lease is released a new one is acquired (T4203). We will
+ # only restart DHCP client if it's option changed, or if it's not
+ # running, but it should be running (e.g. on system startup)
+ if 'dhcp_options_changed' in self._config or not is_systemd_service_active(systemd_service):
+ return self._cmd(f'systemctl restart {systemd_service}')
+ return None
else:
+ if is_systemd_service_active(systemd_service):
+ self._cmd(f'systemctl stop {systemd_service}')
# cleanup old config files
for file in [config_file, options_file, pid_file, lease_file]:
if os.path.isfile(file):
@@ -1284,48 +1286,57 @@ class Interface(Control):
if os.path.isfile(config_file):
os.remove(config_file)
- def set_mirror(self):
+ def set_mirror_redirect(self):
# Please refer to the document for details
# - https://man7.org/linux/man-pages/man8/tc.8.html
# - https://man7.org/linux/man-pages/man8/tc-mirred.8.html
# Depening if we are the source or the target interface of the port
# mirror we need to setup some variables.
source_if = self._config['ifname']
- config = self._config.get('mirror', None)
+ mirror_config = None
+ if 'mirror' in self._config:
+ mirror_config = self._config['mirror']
if 'is_mirror_intf' in self._config:
source_if = next(iter(self._config['is_mirror_intf']))
- config = self._config['is_mirror_intf'][source_if].get('mirror', None)
-
- # Check configuration stored by old perl code before delete T3782/T4056
- if not 'redirect' in self._config and not 'traffic_policy' 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:
- return
-
- for direction, mirror_if in config.items():
- if mirror_if not in interfaces():
- continue
-
- if direction == 'ingress':
- handle = 'ffff: ingress'
- parent = 'ffff:'
- elif direction == 'egress':
- handle = '1: root prio'
- parent = '1:'
-
- # Mirror egress traffic
- mirror_cmd = f'tc qdisc add dev {source_if} handle {handle}; '
- # Export the mirrored traffic to the interface
- mirror_cmd += f'tc filter add dev {source_if} parent {parent} protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {mirror_if}'
- self._popen(mirror_cmd)
+ mirror_config = self._config['is_mirror_intf'][source_if].get('mirror', None)
+
+ redirect_config = None
+
+ # clear existing ingess - ignore errors (e.g. "Error: Cannot find specified
+ # qdisc on specified device") - we simply cleanup all stuff here
+ self._popen(f'tc qdisc del dev {source_if} parent ffff: 2>/dev/null');
+ self._popen(f'tc qdisc del dev {source_if} parent 1: 2>/dev/null');
+
+ # Apply interface mirror policy
+ if mirror_config:
+ for direction, target_if in mirror_config.items():
+ if direction == 'ingress':
+ handle = 'ffff: ingress'
+ parent = 'ffff:'
+ elif direction == 'egress':
+ handle = '1: root prio'
+ parent = '1:'
+
+ # Mirror egress traffic
+ mirror_cmd = f'tc qdisc add dev {source_if} handle {handle}; '
+ # Export the mirrored traffic to the interface
+ mirror_cmd += f'tc filter add dev {source_if} parent {parent} protocol '\
+ f'all prio 10 u32 match u32 0 0 flowid 1:1 action mirred '\
+ f'egress mirror dev {target_if}'
+ _, err = self._popen(mirror_cmd)
+ if err: print('tc qdisc(filter for mirror port failed')
+
+ # Apply interface traffic redirection policy
+ elif 'redirect' in self._config:
+ _, err = self._popen(f'tc qdisc add dev {source_if} handle ffff: ingress')
+ if err: print(f'tc qdisc add for redirect failed!')
+
+ target_if = self._config['redirect']
+ _, err = self._popen(f'tc filter add dev {source_if} parent ffff: protocol '\
+ f'all prio 10 u32 match u32 0 0 flowid 1:1 action mirred '\
+ f'egress redirect dev {target_if}')
+ if err: print('tc filter add for redirect failed')
def set_xdp(self, state):
"""
@@ -1424,9 +1435,6 @@ class Interface(Control):
else:
self.del_addr(addr)
- for addr in new_addr:
- self.add_addr(addr)
-
# start DHCPv6 client when only PD was configured
if dhcpv6pd:
self.set_dhcpv6(True)
@@ -1441,16 +1449,15 @@ class Interface(Control):
# checked before
self.set_vrf(config.get('vrf', ''))
+ # Add this section after vrf T4331
+ for addr in new_addr:
+ self.add_addr(addr)
+
# Configure MSS value for IPv4 TCP connections
tmp = dict_search('ip.adjust_mss', config)
value = tmp if (tmp != None) else '0'
self.set_tcp_ipv4_mss(value)
- # Configure MSS value for IPv6 TCP connections
- tmp = dict_search('ipv6.adjust_mss', config)
- value = tmp if (tmp != None) else '0'
- self.set_tcp_ipv6_mss(value)
-
# Configure ARP cache timeout in milliseconds - has default value
tmp = dict_search('ip.arp_cache_timeout', config)
value = tmp if (tmp != None) else '30'
@@ -1496,6 +1503,18 @@ class Interface(Control):
value = tmp if (tmp != None) else '0'
self.set_ipv4_source_validation(value)
+ # MTU - Maximum Transfer Unit has a default value. It must ALWAYS be set
+ # before mangling any IPv6 option. If MTU is less then 1280 IPv6 will be
+ # automatically disabled by the kernel. Also MTU must be increased before
+ # configuring any IPv6 address on the interface.
+ if 'mtu' in config:
+ self.set_mtu(config.get('mtu'))
+
+ # Configure MSS value for IPv6 TCP connections
+ tmp = dict_search('ipv6.adjust_mss', config)
+ value = tmp if (tmp != None) else '0'
+ self.set_tcp_ipv6_mss(value)
+
# IPv6 forwarding
tmp = dict_search('ipv6.disable_forwarding', config)
value = '0' if (tmp != None) else '1'
@@ -1518,10 +1537,6 @@ class Interface(Control):
value = tmp if (tmp != None) else '1'
self.set_ipv6_dad_messages(value)
- # MTU - Maximum Transfer Unit
- if 'mtu' in config:
- self.set_mtu(config.get('mtu'))
-
# Delete old IPv6 EUI64 addresses before changing MAC
for addr in (dict_search('ipv6.address.eui64_old', config) or []):
self.del_ipv6_eui64_address(addr)
@@ -1546,8 +1561,8 @@ class Interface(Control):
# eXpress Data Path - highly experimental
self.set_xdp('xdp' in config)
- # configure port mirror
- self.set_mirror()
+ # configure interface mirror or redirection target
+ self.set_mirror_redirect()
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
@@ -1706,6 +1721,3 @@ class VLANIf(Interface):
return None
return super().set_admin_state(state)
-
- def set_mirror(self):
- return
diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py
index 192c12f5c..b3babfadc 100644
--- a/python/vyos/ifconfig/loopback.py
+++ b/python/vyos/ifconfig/loopback.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2022 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
@@ -23,7 +23,6 @@ class LoopbackIf(Interface):
"""
_persistent_addresses = ['127.0.0.1/8', '::1/128']
iftype = 'loopback'
-
definition = {
**Interface.definition,
**{
@@ -32,6 +31,7 @@ class LoopbackIf(Interface):
'bridgeable': True,
}
}
+
def remove(self):
"""
Loopback interface can not be deleted from operating system. We can
@@ -57,12 +57,14 @@ class LoopbackIf(Interface):
interface setup code and provide a single point of entry when workin
on any interface. """
- addr = config.get('address', [])
+ address = config.get('address', [])
# We must ensure that the loopback addresses are never deleted from the system
- addr += self._persistent_addresses
+ for tmp in self._persistent_addresses:
+ if tmp not in address:
+ address.append(tmp)
# Update IP address entry in our dictionary
- config.update({'address' : addr})
+ config.update({'address' : address})
# call base class
super().update(config)
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 0c5282db4..516a19f24 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2022 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
@@ -68,6 +68,16 @@ class VXLANIf(Interface):
'vni' : 'id',
}
+ # IPv6 flowlabels can only be used on IPv6 tunnels, thus we need to
+ # ensure that at least the first remote IP address is passed to the
+ # tunnel creation command. Subsequent tunnel remote addresses can later
+ # be added to the FDB
+ remote_list = None
+ if 'remote' in self.config:
+ # skip first element as this is already configured as remote
+ remote_list = self.config['remote'][1:]
+ self.config['remote'] = self.config['remote'][0]
+
cmd = 'ip link add {ifname} type {type} dstport {port}'
for vyos_key, iproute2_key in mapping.items():
# dict_search will return an empty dict "{}" for valueless nodes like
@@ -82,3 +92,10 @@ class VXLANIf(Interface):
self._cmd(cmd.format(**self.config))
# interface is always A/D down. It needs to be enabled explicitly
self.set_admin_state('down')
+
+ # VXLAN tunnel is always recreated on any change - see interfaces-vxlan.py
+ if remote_list:
+ for remote in remote_list:
+ cmd = f'bridge fdb append to 00:00:00:00:00:00 dst {remote} ' \
+ 'port {port} dev {ifname}'
+ self._cmd(cmd.format(**self.config))
diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py
index 748b6e02d..88eaa772b 100644
--- a/python/vyos/ifconfig/wireless.py
+++ b/python/vyos/ifconfig/wireless.py
@@ -49,10 +49,10 @@ class WiFiIf(Interface):
on any interface. """
# We can not call add_to_bridge() until wpa_supplicant is running, thus
- # we will remove the key from the config dict and react to this specal
- # case in thie derived class.
+ # we will remove the key from the config dict and react to this special
+ # case in this derived class.
# re-add ourselves to any bridge we might have fallen out of
- bridge_member = ''
+ bridge_member = None
if 'is_bridge_member' in config:
bridge_member = config['is_bridge_member']
del config['is_bridge_member']
diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py
index 4574bb6d1..a2e0daabd 100644
--- a/python/vyos/migrator.py
+++ b/python/vyos/migrator.py
@@ -195,7 +195,7 @@ class Migrator(object):
# This will force calling all migration scripts:
cfg_versions = {}
- sys_versions = systemversions.get_system_versions()
+ sys_versions = systemversions.get_system_component_version()
# save system component versions in json file for easy reference
self.save_json_record(sys_versions)
diff --git a/python/vyos/pki.py b/python/vyos/pki.py
index 68ad73bf2..0b916eaae 100644
--- a/python/vyos/pki.py
+++ b/python/vyos/pki.py
@@ -331,3 +331,29 @@ def verify_certificate(cert, ca_cert):
return True
except InvalidSignature:
return False
+
+# Certificate chain
+
+def find_parent(cert, ca_certs):
+ for ca_cert in ca_certs:
+ if verify_certificate(cert, ca_cert):
+ return ca_cert
+ return None
+
+def find_chain(cert, ca_certs):
+ remaining = ca_certs.copy()
+ chain = [cert]
+
+ while remaining:
+ parent = find_parent(chain[-1], remaining)
+ if parent is None:
+ # No parent in the list of remaining certificates or there's a circular dependency
+ break
+ elif parent == chain[-1]:
+ # Self-signed: must be root CA (end of chain)
+ break
+ else:
+ remaining.remove(parent)
+ chain.append(parent)
+
+ return chain
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index aa62ac60d..66044fa52 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -83,8 +83,7 @@ def check_storage(path, size):
directory = path if os.path.isdir(path) else (os.path.dirname(os.path.expanduser(path)) or os.getcwd())
# `size` can be None or 0 to indicate unknown size.
if not size:
- print_error('Warning: Cannot determine size of remote file.')
- print_error('Bravely continuing regardless.')
+ print_error('Warning: Cannot determine size of remote file. Bravely continuing regardless.')
return
if size < 1024 * 1024:
@@ -227,7 +226,7 @@ class HttpC:
r.raise_for_status()
# If the request got redirected, keep the last URL we ended up with.
final_urlstring = r.url
- if r.history:
+ if r.history and self.progressbar:
print_error('Redirecting to ' + final_urlstring)
# Check for the prospective file size.
try:
@@ -317,11 +316,12 @@ def friendly_download(local_path, urlstring, source_host='', source_port=0):
sys.exit(1)
except:
import traceback
+ print_error(f'Failed to download {urlstring}.')
# There are a myriad different reasons a download could fail.
# SSH errors, FTP errors, I/O errors, HTTP errors (403, 404...)
# We omit the scary stack trace but print the error nevertheless.
- print_error(f'Failed to download {urlstring}.')
- traceback.print_exception(*sys.exc_info()[:2], None)
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ traceback.print_exception(exc_type, exc_value, None, 0, None, False)
sys.exit(1)
else:
print_error('Download complete.')
diff --git a/python/vyos/systemversions.py b/python/vyos/systemversions.py
index 9b3f4f413..f2da76d4f 100644
--- a/python/vyos/systemversions.py
+++ b/python/vyos/systemversions.py
@@ -17,7 +17,10 @@ import os
import re
import sys
import vyos.defaults
+from vyos.xml import component_version
+# legacy version, reading from the file names in
+# /opt/vyatta/etc/config-migrate/current
def get_system_versions():
"""
Get component versions from running system; critical failure if
@@ -37,3 +40,7 @@ def get_system_versions():
system_versions[pair[0]] = int(pair[1])
return system_versions
+
+# read from xml cache
+def get_system_component_version():
+ return component_version()
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 6f65c6c98..132f5ddde 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -18,7 +18,7 @@ import os
from jinja2 import Environment
from jinja2 import FileSystemLoader
-
+from jinja2 import ChainableUndefined
from vyos.defaults import directories
from vyos.util import chmod
from vyos.util import chown
@@ -27,6 +27,7 @@ from vyos.util import makedir
# Holds template filters registered via register_filter()
_FILTERS = {}
+_TESTS = {}
# reuse Environments with identical settings to improve performance
@functools.lru_cache(maxsize=2)
@@ -42,8 +43,10 @@ def _get_environment(location=None):
cache_size=100,
loader=loc_loader,
trim_blocks=True,
+ undefined=ChainableUndefined,
)
env.filters.update(_FILTERS)
+ env.tests.update(_TESTS)
return env
@@ -67,6 +70,26 @@ def register_filter(name, func=None):
_FILTERS[name] = func
return func
+def register_test(name, func=None):
+ """Register a function to be available as test in templates under given name.
+
+ It can also be used as a decorator, see below in this module for examples.
+
+ :raise RuntimeError:
+ when trying to register a test after a template has been rendered already
+ :raise ValueError: when trying to register a name which was taken already
+ """
+ if func is None:
+ return functools.partial(register_test, name)
+ if _get_environment.cache_info().currsize:
+ raise RuntimeError(
+ "Tests can only be registered before rendering the first template"
+ )
+ if name in _TESTS:
+ raise ValueError(f"A test with name {name!r} was registered already")
+ _TESTS[name] = func
+ return func
+
def render_to_string(template, content, formater=None, location=None):
"""Render a template from the template directory, raise on any errors.
@@ -127,6 +150,14 @@ def render(
##################################
# Custom template filters follow #
##################################
+@register_filter('force_to_list')
+def force_to_list(value):
+ """ Convert scalars to single-item lists and leave lists untouched """
+ if isinstance(value, list):
+ return value
+ else:
+ return [value]
+
@register_filter('ip_from_cidr')
def ip_from_cidr(prefix):
""" Take an IPv4/IPv6 CIDR host and strip cidr mask.
@@ -516,6 +547,19 @@ def nft_rule(rule_conf, fw_name, rule_id, ip_name='ip'):
from vyos.firewall import parse_rule
return parse_rule(rule_conf, fw_name, rule_id, ip_name)
+@register_filter('nft_default_rule')
+def nft_default_rule(fw_conf, fw_name):
+ output = ['counter']
+ default_action = fw_conf.get('default_action', 'accept')
+
+ if 'enable_default_log' in fw_conf:
+ action_suffix = default_action[:1].upper()
+ output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}] "')
+
+ output.append(nft_action(default_action))
+ output.append(f'comment "{fw_name} default-action {default_action}"')
+ return " ".join(output)
+
@register_filter('nft_state_policy')
def nft_state_policy(conf, state, ipv6=False):
out = [f'ct state {state}']
@@ -535,6 +579,7 @@ def nft_intra_zone_action(zone_conf, ipv6=False):
if 'intra_zone_filtering' in zone_conf:
intra_zone = zone_conf['intra_zone_filtering']
fw_name = 'ipv6_name' if ipv6 else 'name'
+ name_prefix = 'NAME6_' if ipv6 else 'NAME_'
if 'action' in intra_zone:
if intra_zone['action'] == 'accept':
@@ -542,5 +587,57 @@ def nft_intra_zone_action(zone_conf, ipv6=False):
return intra_zone['action']
elif dict_search_args(intra_zone, 'firewall', fw_name):
name = dict_search_args(intra_zone, 'firewall', fw_name)
- return f'jump {name}'
+ return f'jump {name_prefix}{name}'
return 'return'
+
+@register_test('vyos_defined')
+def vyos_defined(value, test_value=None, var_type=None):
+ """
+ Jinja2 plugin to test if a variable is defined and not none - vyos_defined
+ will test value if defined and is not none and return true or false.
+
+ If test_value is supplied, the value must also pass == test_value to return true.
+ If var_type is supplied, the value must also be of the specified class/type
+
+ Examples:
+ 1. Test if var is defined and not none:
+ {% if foo is vyos_defined %}
+ ...
+ {% endif %}
+
+ 2. Test if variable is defined, not none and has value "something"
+ {% if bar is vyos_defined("something") %}
+ ...
+ {% endif %}
+
+ Parameters
+ ----------
+ value : any
+ Value to test from ansible
+ test_value : any, optional
+ Value to test in addition of defined and not none, by default None
+ var_type : ['float', 'int', 'str', 'list', 'dict', 'tuple', 'bool'], optional
+ Type or Class to test for
+
+ Returns
+ -------
+ boolean
+ True if variable matches criteria, False in other cases.
+
+ Implementation inspired and re-used from https://github.com/aristanetworks/ansible-avd/
+ """
+
+ from jinja2 import Undefined
+
+ if isinstance(value, Undefined) or value is None:
+ # Invalid value - return false
+ return False
+ elif test_value is not None and value != test_value:
+ # Valid value but not matching the optional argument
+ return False
+ elif str(var_type).lower() in ['float', 'int', 'str', 'list', 'dict', 'tuple', 'bool'] and str(var_type).lower() != type(value).__name__:
+ # Invalid class - return false
+ return False
+ else:
+ # Valid value and is matching optional argument if provided - return true
+ return True
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 954c6670d..de55e108b 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -774,6 +774,14 @@ def dict_search_recursive(dict_object, key):
for x in dict_search_recursive(j, key):
yield x
+def get_bridge_fdb(interface):
+ """ Returns the forwarding database entries for a given interface """
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ return None
+ from json import loads
+ tmp = loads(cmd(f'bridge -j fdb show dev {interface}'))
+ return tmp
+
def get_interface_config(interface):
""" Returns the used encapsulation protocol for given interface.
If interface does not exist, None is returned.
@@ -952,14 +960,23 @@ def install_into_config(conf, config_paths, override_prompt=True):
return None
count = 0
+ failed = []
for path in config_paths:
if override_prompt and conf.exists(path) and not conf.is_multi(path):
if not ask_yes_no(f'Config node "{node}" already exists. Do you want to overwrite it?'):
continue
- cmd(f'/opt/vyatta/sbin/my_set {path}')
- count += 1
+ try:
+ cmd(f'/opt/vyatta/sbin/my_set {path}')
+ count += 1
+ except:
+ failed.append(path)
+
+ if failed:
+ print(f'Failed to install {len(failed)} value(s). Commands to manually install:')
+ for path in failed:
+ print(f'set {path}')
if count > 0:
print(f'{count} value(s) installed. Use "compare" to see the pending changes, and "commit" to apply.')
@@ -972,6 +989,11 @@ def is_wwan_connected(interface):
if not interface.startswith('wwan'):
raise ValueError(f'Specified interface "{interface}" is not a WWAN interface')
+ # ModemManager is required for connection(s) - if service is not running,
+ # there won't be any connection at all!
+ if not is_systemd_service_active('ModemManager.service'):
+ return False
+
modem = interface.lstrip('wwan')
tmp = cmd(f'mmcli --modem {modem} --output-json')
@@ -988,3 +1010,17 @@ def boot_configuration_complete() -> bool:
if os.path.isfile(config_status):
return True
return False
+
+def sysctl_read(name):
+ """ Read and return current value of sysctl() option """
+ tmp = cmd(f'sysctl {name}')
+ return tmp.split()[-1]
+
+def sysctl_write(name, value):
+ """ Change value via sysctl() - return True if changed, False otherwise """
+ tmp = cmd(f'sysctl {name}')
+ # last list index contains the actual value - only write if value differs
+ if sysctl_read(name) != str(value):
+ call(f'sysctl -wq {name}={value}')
+ return True
+ return False
diff --git a/python/vyos/validate.py b/python/vyos/validate.py
index 0dad2a6cb..e005da0e4 100644
--- a/python/vyos/validate.py
+++ b/python/vyos/validate.py
@@ -43,19 +43,14 @@ def _are_same_ip(one, two):
s_two = AF_INET if is_ipv4(two) else AF_INET6
return inet_pton(f_one, one) == inet_pton(f_one, two)
-def is_intf_addr_assigned(intf, addr):
- if '/' in addr:
- ip,mask = addr.split('/')
- return _is_intf_addr_assigned(intf, ip, mask)
- return _is_intf_addr_assigned(intf, addr)
-
-def _is_intf_addr_assigned(intf, address, netmask=None):
+def is_intf_addr_assigned(intf, address) -> bool:
"""
Verify if the given IPv4/IPv6 address is assigned to specific interface.
It can check both a single IP address (e.g. 192.0.2.1 or a assigned CIDR
address 192.0.2.1/24.
"""
from vyos.template import is_ipv4
+
from netifaces import ifaddresses
from netifaces import AF_INET
from netifaces import AF_INET6
@@ -76,6 +71,9 @@ def _is_intf_addr_assigned(intf, address, netmask=None):
addr_type = AF_INET if is_ipv4(address) else AF_INET6
# Check every IP address on this interface for a match
+ netmask = None
+ if '/' in address:
+ address, netmask = address.split('/')
for ip in ifaces.get(addr_type,[]):
# ip can have the interface name in the 'addr' field, we need to remove it
# {'addr': 'fe80::a00:27ff:fec5:f821%eth2', 'netmask': 'ffff:ffff:ffff:ffff::'}
@@ -99,14 +97,20 @@ def _is_intf_addr_assigned(intf, address, netmask=None):
return False
-def is_addr_assigned(addr):
- """
- Verify if the given IPv4/IPv6 address is assigned to any interface
- """
+def is_addr_assigned(ip_address, vrf=None) -> bool:
+ """ Verify if the given IPv4/IPv6 address is assigned to any interfac """
from netifaces import interfaces
- for intf in interfaces():
- tmp = is_intf_addr_assigned(intf, addr)
- if tmp == True:
+ from vyos.util import get_interface_config
+ from vyos.util import dict_search
+ for interface in interfaces():
+ # Check if interface belongs to the requested VRF, if this is not the
+ # case there is no need to proceed with this data set - continue loop
+ # with next element
+ tmp = get_interface_config(interface)
+ if dict_search('master', tmp) != vrf:
+ continue
+
+ if is_intf_addr_assigned(interface, ip_address):
return True
return False
diff --git a/python/vyos/xml/__init__.py b/python/vyos/xml/__init__.py
index e0eacb2d1..6db446a40 100644
--- a/python/vyos/xml/__init__.py
+++ b/python/vyos/xml/__init__.py
@@ -46,8 +46,8 @@ def is_tag(lpath):
def is_leaf(lpath, flat=True):
return load_configuration().is_leaf(lpath, flat)
-def component_versions():
- return load_configuration().component_versions()
+def component_version():
+ return load_configuration().component_version()
def defaults(lpath, flat=False):
return load_configuration().defaults(lpath, flat)
diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py
index 5e0d5282c..bc3892b42 100644
--- a/python/vyos/xml/definition.py
+++ b/python/vyos/xml/definition.py
@@ -249,10 +249,11 @@ class XML(dict):
# @lru_cache(maxsize=100)
# XXX: need to use cachetool instead - for later
- def component_versions(self) -> dict:
- sort_component = sorted(self[kw.component_version].items(),
- key = lambda kv: kv[0])
- return dict(sort_component)
+ def component_version(self) -> dict:
+ d = {}
+ for k in sorted(self[kw.component_version]):
+ d[k] = int(self[kw.component_version][k])
+ return d
def defaults(self, lpath, flat):
d = self[kw.default]
diff --git a/scripts/build-command-templates b/scripts/build-command-templates
index d8abb0a13..876f5877c 100755
--- a/scripts/build-command-templates
+++ b/scripts/build-command-templates
@@ -117,7 +117,7 @@ def collect_validators(ve):
return regex_args + " " + validator_args
-def get_properties(p):
+def get_properties(p, default=None):
props = {}
if p is None:
@@ -125,7 +125,12 @@ def get_properties(p):
# Get the help string
try:
- props["help"] = p.find("help").text
+ help = p.find("help").text
+ if default != None:
+ # DNS forwarding for instance has multiple defaults - specified as whitespace separated list
+ tmp = ', '.join(default.text.split())
+ help += f' (default: {tmp})'
+ props["help"] = help
except:
pass
@@ -134,7 +139,11 @@ def get_properties(p):
vhe = p.findall("valueHelp")
vh = []
for v in vhe:
- vh.append( (v.find("format").text, v.find("description").text) )
+ format = v.find("format").text
+ description = v.find("description").text
+ if default != None and default.text == format:
+ description += f' (default)'
+ vh.append( (format, description) )
props["val_help"] = vh
except:
props["val_help"] = []
@@ -271,7 +280,7 @@ def process_node(n, tmpl_dir):
print("Name of the node: {0}. Created directory: {1}\n".format(name, "/".join(my_tmpl_dir)), end="")
os.makedirs(make_path(my_tmpl_dir), exist_ok=True)
- props = get_properties(props_elem)
+ props = get_properties(props_elem, n.find("defaultValue"))
if owner:
props["owner"] = owner
# Type should not be set for non-tag, non-leaf nodes
diff --git a/smoketest/configs/basic-vyos b/smoketest/configs/basic-vyos
new file mode 100644
index 000000000..493feed5b
--- /dev/null
+++ b/smoketest/configs/basic-vyos
@@ -0,0 +1,88 @@
+interfaces {
+ ethernet eth0 {
+ address 192.168.0.1/24
+ duplex auto
+ smp-affinity auto
+ speed auto
+ }
+ ethernet eth1 {
+ address 100.64.0.0/31
+ duplex auto
+ smp-affinity auto
+ speed auto
+ }
+ loopback lo {
+ }
+}
+protocols {
+ static {
+ route 0.0.0.0/0 {
+ next-hop 100.64.0.1 {
+ }
+ }
+ }
+}
+service {
+ dhcp-server {
+ shared-network-name LAN {
+ authoritative
+ subnet 192.168.0.0/24 {
+ default-router 192.168.0.1
+ dns-server 192.168.0.1
+ domain-name vyos.net
+ domain-search vyos.net
+ range LANDynamic {
+ start 192.168.0.20
+ stop 192.168.0.240
+ }
+ }
+ }
+ }
+ dns {
+ forwarding {
+ allow-from 192.168.0.0/16
+ cache-size 10000
+ dnssec off
+ listen-address 192.168.0.1
+ }
+ }
+ ssh {
+ ciphers aes128-ctr,aes192-ctr,aes256-ctr
+ ciphers chacha20-poly1305@openssh.com,rijndael-cbc@lysator.liu.se
+ listen-address 192.168.0.1
+ key-exchange curve25519-sha256@libssh.org
+ key-exchange diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256
+ port 22
+ }
+}
+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 ""
+ }
+ }
+ }
+ name-server 192.168.0.1
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ }
+ }
+ 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@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */
+/* Release version: 1.2.6 */
diff --git a/smoketest/configs/dialup-router-complex b/smoketest/configs/dialup-router-complex
index fef79ea56..ac5ff5e99 100644
--- a/smoketest/configs/dialup-router-complex
+++ b/smoketest/configs/dialup-router-complex
@@ -267,6 +267,22 @@ firewall {
}
protocol udp
}
+ rule 800 {
+ action drop
+ description "SSH anti brute force"
+ destination {
+ port ssh
+ }
+ log enable
+ protocol tcp
+ recent {
+ count 4
+ time 60
+ }
+ state {
+ new enable
+ }
+ }
}
name DMZ-WAN {
default-action accept
@@ -482,6 +498,9 @@ firewall {
destination {
port 110,995
}
+ limit {
+ rate "10/minute"
+ }
protocol tcp
}
rule 123 {
diff --git a/smoketest/configs/dialup-router-medium-vpn b/smoketest/configs/dialup-router-medium-vpn
index af7c075e4..63d955738 100644
--- a/smoketest/configs/dialup-router-medium-vpn
+++ b/smoketest/configs/dialup-router-medium-vpn
@@ -6,6 +6,15 @@ firewall {
ipv6-src-route disable
ip-src-route disable
log-martians enable
+ name test_tcp_flags {
+ rule 1 {
+ action drop
+ protocol tcp
+ tcp {
+ flags SYN,ACK,!RST,!FIN
+ }
+ }
+ }
options {
interface vtun0 {
adjust-mss 1380
@@ -83,6 +92,7 @@ interfaces {
}
policy {
route LAN-POLICY-BASED-ROUTING
+ ipv6-route LAN6-POLICY-BASED-ROUTING
}
smp-affinity auto
speed auto
@@ -383,6 +393,29 @@ nat {
}
}
policy {
+ ipv6-route LAN6-POLICY-BASED-ROUTING {
+ rule 10 {
+ destination {
+ }
+ disable
+ set {
+ table 10
+ }
+ source {
+ address 2002::1
+ }
+ }
+ rule 20 {
+ destination {
+ }
+ set {
+ table 100
+ }
+ source {
+ address 2008::f
+ }
+ }
+ }
prefix-list user2-routes {
rule 1 {
action permit
diff --git a/smoketest/configs/ipv6-disable b/smoketest/configs/ipv6-disable
new file mode 100644
index 000000000..da41e9020
--- /dev/null
+++ b/smoketest/configs/ipv6-disable
@@ -0,0 +1,83 @@
+interfaces {
+ ethernet eth0 {
+ duplex auto
+ smp-affinity auto
+ speed auto
+ vif 201 {
+ address 172.18.201.10/24
+ }
+ vif 202 {
+ address 172.18.202.10/24
+ }
+ vif 203 {
+ address 172.18.203.10/24
+ }
+ vif 204 {
+ address 172.18.204.10/24
+ }
+ }
+}
+protocols {
+ static {
+ route 0.0.0.0/0 {
+ next-hop 172.18.201.254 {
+ distance 10
+ }
+ next-hop 172.18.202.254 {
+ distance 20
+ }
+ next-hop 172.18.203.254 {
+ distance 30
+ }
+ next-hop 172.18.204.254 {
+ distance 40
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 200
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos.net
+ host-name vyos
+ ipv6 {
+ disable
+ }
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ level admin
+ }
+ }
+ name-server 172.16.254.20
+ name-server 172.16.254.30
+ ntp {
+ server 172.16.254.20 {
+ }
+ server 172.16.254.30 {
+ }
+ }
+ 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@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */
+/* Release version: 1.2.6 */
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 9de961249..ba5acf5d6 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -56,6 +56,7 @@ def is_mirrored_to(interface, mirror_if, qdisc):
class BasicInterfaceTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
+ _test_dhcp = False
_test_ip = False
_test_mtu = False
_test_vlan = False
@@ -96,6 +97,35 @@ class BasicInterfaceTest:
for intf in self._interfaces:
self.assertNotIn(intf, interfaces())
+ # No daemon that was started during a test should remain running
+ for daemon in ['dhcp6c', 'dhclient']:
+ self.assertFalse(process_named_running(daemon))
+
+ def test_dhcp_disable_interface(self):
+ if not self._test_dhcp:
+ self.skipTest('not supported')
+
+ # When interface is configured as admin down, it must be admin down
+ # even when dhcpc starts on the given interface
+ for interface in self._interfaces:
+ self.cli_set(self._base_path + [interface, 'disable'])
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'disable'])
+
+ # Also enable DHCP (ISC DHCP always places interface in admin up
+ # state so we check that we do not start DHCP client.
+ # https://phabricator.vyos.net/T2767
+ self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
+
+ self.cli_commit()
+
+ # Validate interface state
+ for interface in self._interfaces:
+ flags = read_file(f'/sys/class/net/{interface}/flags')
+ self.assertEqual(int(flags, 16) & 1, 0)
+
def test_span_mirror(self):
if not self._mirror_interfaces:
self.skipTest('not supported')
diff --git a/smoketest/scripts/cli/test_component_version.py b/smoketest/scripts/cli/test_component_version.py
new file mode 100755
index 000000000..777379bdd
--- /dev/null
+++ b/smoketest/scripts/cli/test_component_version.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 vyos.systemversions import get_system_versions, get_system_component_version
+
+# After T3474, component versions should be updated in the files in
+# vyos-1x/interface-definitions/include/version/
+# This test verifies that the legacy version in curver_DATA does not exceed
+# that in the xml cache.
+class TestComponentVersion(unittest.TestCase):
+ def setUp(self):
+ self.legacy_d = get_system_versions()
+ self.xml_d = get_system_component_version()
+
+ def test_component_version(self):
+ self.assertTrue(set(self.legacy_d).issubset(set(self.xml_d)))
+ for k, v in self.legacy_d.items():
+ self.assertTrue(v <= self.xml_d[k])
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 2b3b354ba..16b020e07 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -46,6 +46,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_groups(self):
+ self.cli_set(['firewall', 'group', 'mac-group', 'smoketest_mac', 'mac-address', '00:01:02:03:04:05'])
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '123'])
@@ -53,15 +54,18 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'group', 'mac-group', 'smoketest_mac'])
self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
self.cli_commit()
nftables_search = [
- ['iifname "eth0"', 'jump smoketest'],
- ['ip saddr { 172.16.99.0/24 }', 'ip daddr 172.16.10.10', 'tcp dport { 53, 123 }', 'return'],
+ ['iifname "eth0"', 'jump NAME_smoketest'],
+ ['ip saddr { 172.16.99.0/24 }', 'ip daddr 172.16.10.10', 'th dport { 53, 123 }', 'return'],
+ ['ether saddr { 00:01:02:03:04:05 }', 'return']
]
nftables_output = cmd('sudo nft list table ip filter')
@@ -72,7 +76,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
if all(item in line for item in search):
matched = True
break
- self.assertTrue(matched)
+ self.assertTrue(matched, msg=search)
def test_basic_rules(self):
self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
@@ -80,17 +84,24 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'reject'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'protocol', 'tcp'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'destination', 'port', '8888'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'tcp', 'flags', 'syn'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'destination', 'port', '22'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'limit', 'rate', '5/minute'])
self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
self.cli_commit()
nftables_search = [
- ['iifname "eth0"', 'jump smoketest'],
+ ['iifname "eth0"', 'jump NAME_smoketest'],
['saddr 172.16.20.10', 'daddr 172.16.10.10', 'return'],
- ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'],
+ ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'reject'],
+ ['tcp dport { 22 }', 'limit rate 5/minute', 'return'],
['smoketest default-action', 'drop']
]
@@ -102,7 +113,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
if all(item in line for item in search):
matched = True
break
- self.assertTrue(matched)
+ self.assertTrue(matched, msg=search)
def test_basic_rules_ipv6(self):
self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'default-action', 'drop'])
@@ -118,7 +129,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
nftables_search = [
- ['iifname "eth0"', 'jump v6-smoketest'],
+ ['iifname "eth0"', 'jump NAME6_v6-smoketest'],
['saddr 2002::1', 'daddr 2002::1:1', 'return'],
['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'],
['smoketest default-action', 'drop']
@@ -132,7 +143,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
if all(item in line for item in search):
matched = True
break
- self.assertTrue(matched)
+ self.assertTrue(matched, msg=search)
def test_state_policy(self):
self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py
index a37ce7e64..68905e447 100755
--- a/smoketest/scripts/cli/test_ha_vrrp.py
+++ b/smoketest/scripts/cli/test_ha_vrrp.py
@@ -166,5 +166,35 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
self.assertIn(f'{group}', config)
+ def test_04_exclude_vrrp_interface(self):
+ group = 'VyOS-WAN'
+ none_vrrp_interface = 'eth2'
+ vlan_id = '24'
+ vip = '100.64.24.1/24'
+ vip_dev = '192.0.2.2/24'
+ vrid = '150'
+ group_base = base_path + ['vrrp', 'group', group]
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', '100.64.24.11/24'])
+ self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}'])
+ self.cli_set(group_base + ['address', vip])
+ self.cli_set(group_base + ['address', vip_dev, 'interface', none_vrrp_interface])
+ self.cli_set(group_base + ['track', 'exclude-vrrp-interface'])
+ self.cli_set(group_base + ['track', 'interface', none_vrrp_interface])
+ self.cli_set(group_base + ['vrid', vrid])
+
+ # commit changes
+ self.cli_commit()
+
+ config = getConfig(f'vrrp_instance {group}')
+
+ self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config)
+ self.assertIn(f'virtual_router_id {vrid}', config)
+ self.assertIn(f'dont_track_primary', config)
+ self.assertIn(f' {vip}', config)
+ self.assertIn(f' {vip_dev} dev {none_vrrp_interface}', config)
+ self.assertIn(f'track_interface', config)
+ self.assertIn(f' {none_vrrp_interface}', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index 86000553e..4f2fe979a 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -28,6 +28,7 @@ from vyos.util import read_file
class BondingInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
@@ -36,7 +37,6 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
cls._test_vlan = True
cls._test_qinq = True
cls._base_path = ['interfaces', 'bonding']
- cls._interfaces = ['bond0']
cls._mirror_interfaces = ['dum21354']
cls._members = []
@@ -52,6 +52,7 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
cls._options['bond0'] = []
for member in cls._members:
cls._options['bond0'].append(f'member interface {member}')
+ cls._interfaces = list(cls._options)
# call base-classes classmethod
super(cls, cls).setUpClass()
@@ -150,5 +151,19 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
defined_policy = read_file(f'/sys/class/net/{interface}/bonding/xmit_hash_policy').split()
self.assertEqual(defined_policy[0], hash_policy)
+ def test_bonding_multi_use_member(self):
+ # Define available bonding hash policies
+ for interface in ['bond10', 'bond20']:
+ for member in self._members:
+ self.cli_set(self._base_path + [interface, 'member', 'interface', member])
+
+ # check validate() - can not use the same member interfaces multiple times
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(self._base_path + ['bond20'])
+
+ self.cli_commit()
+
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 4f7e03298..f2e111425 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -31,6 +31,7 @@ from vyos.validate import is_intf_addr_assigned
class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index 6d80e4c96..ee7649af8 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -21,29 +21,73 @@ import unittest
from base_interfaces_test import BasicInterfaceTest
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
+from vyos.pki import CERT_BEGIN
from vyos.util import cmd
from vyos.util import process_named_running
from vyos.util import read_file
-cert_data = """
-MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
-WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
-bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
-MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
-BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
-UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
-01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
-QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
-BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
-+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
-ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
-+dm/LDnp7C0=
+server_ca_root_cert_data = """
+MIIBcTCCARagAwIBAgIUDcAf1oIQV+6WRaW7NPcSnECQ/lUwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjBa
+Fw0zMjAyMTUxOTQxMjBaMB4xHDAaBgNVBAMME1Z5T1Mgc2VydmVyIHJvb3QgQ0Ew
+WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ0y24GzKQf4aM2Ir12tI9yITOIzAUj
+ZXyJeCmYI6uAnyAMqc4Q4NKyfq3nBi4XP87cs1jlC1P2BZ8MsjL5MdGWozIwMDAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRwC/YaieMEnjhYa7K3Flw/o0SFuzAK
+BggqhkjOPQQDAgNJADBGAiEAh3qEj8vScsjAdBy5shXzXDVVOKWCPTdGrPKnu8UW
+a2cCIQDlDgkzWmn5ujc5ATKz1fj+Se/aeqwh4QyoWCVTFLIxhQ==
"""
-key_data = """
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
-2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
-u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+server_ca_intermediate_cert_data = """
+MIIBmTCCAT+gAwIBAgIUNzrtHzLmi3QpPK57tUgCnJZhXXQwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
+Fw0zMjAyMTUxOTQxMjFaMCYxJDAiBgNVBAMMG1Z5T1Mgc2VydmVyIGludGVybWVk
+aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEl2nJ1CzoqPV6hWII2m
+eGN/uieU6wDMECTk/LgG8CCCSYb488dibUiFN/1UFsmoLIdIhkx/6MUCYh62m8U2
+WNujUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMV3YwH88I5gFsFUibbQ
+kMR0ECPsMB8GA1UdIwQYMBaAFHAL9hqJ4wSeOFhrsrcWXD+jRIW7MAoGCCqGSM49
+BAMCA0gAMEUCIQC/ahujD9dp5pMMCd3SZddqGC9cXtOwMN0JR3e5CxP13AIgIMQm
+jMYrinFoInxmX64HfshYqnUY8608nK9D2BNPOHo=
+"""
+
+client_ca_root_cert_data = """
+MIIBcDCCARagAwIBAgIUZmoW2xVdwkZSvglnkCq0AHKa6zIwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
+Fw0zMjAyMTUxOTQxMjFaMB4xHDAaBgNVBAMME1Z5T1MgY2xpZW50IHJvb3QgQ0Ew
+WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATUpKXzQk2NOVKDN4VULk2yw4mOKPvn
+mg947+VY7lbpfOfAUD0QRg95qZWCw899eKnXp/U4TkAVrmEKhUb6OJTFozIwMDAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTXu6xGWUl25X3sBtrhm3BJSICIATAK
+BggqhkjOPQQDAgNIADBFAiEAnTzEwuTI9bz2Oae3LZbjP6f/f50KFJtjLZFDbQz7
+DpYCIDNRHV8zBUibC+zg5PqMpQBKd/oPfNU76nEv6xkp/ijO
+"""
+
+client_ca_intermediate_cert_data = """
+MIIBmDCCAT+gAwIBAgIUJEMdotgqA7wU4XXJvEzDulUAGqgwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjJa
+Fw0zMjAyMTUxOTQxMjJaMCYxJDAiBgNVBAMMG1Z5T1MgY2xpZW50IGludGVybWVk
+aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGyIVIi217s9j3O+WQ2b
+6R65/Z0ZjQpELxPjBRc0CA0GFCo+pI5EvwI+jNFArvTAJ5+ZdEWUJ1DQhBKDDQdI
+avCjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOUS8oNJjChB1Rb9Blcl
+ETvziHJ9MB8GA1UdIwQYMBaAFNe7rEZZSXblfewG2uGbcElIgIgBMAoGCCqGSM49
+BAMCA0cAMEQCIArhaxWgRsAUbEeNHD/ULtstLHxw/P97qPUSROLQld53AiBjgiiz
+9pDfISmpekZYz6bIDWRIR0cXUToZEMFNzNMrQg==
+"""
+
+client_cert_data = """
+MIIBmTCCAUCgAwIBAgIUV5T77XdE/tV82Tk4Vzhp5BIFFm0wCgYIKoZIzj0EAwIw
+JjEkMCIGA1UEAwwbVnlPUyBjbGllbnQgaW50ZXJtZWRpYXRlIENBMB4XDTIyMDIx
+NzE5NDEyMloXDTMyMDIxNTE5NDEyMlowIjEgMB4GA1UEAwwXVnlPUyBjbGllbnQg
+Y2VydGlmaWNhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARuyynqfc/qJj5e
+KJ03oOH8X4Z8spDeAPO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAh
+CIhytmJao1AwTjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTIFKrxZ+PqOhYSUqnl
+TGCUmM7wTjAfBgNVHSMEGDAWgBTlEvKDSYwoQdUW/QZXJRE784hyfTAKBggqhkjO
+PQQDAgNHADBEAiAvO8/jvz05xqmP3OXD53XhfxDLMIxzN4KPoCkFqvjlhQIgIHq2
+/geVx3rAOtSps56q/jiDouN/aw01TdpmGKVAa9U=
+"""
+
+client_key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxaxAQsJwjoOCByQE
++qSYKtKtJzbdbOnTsKNSrfgkFH6hRANCAARuyynqfc/qJj5eKJ03oOH8X4Z8spDe
+APO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAhCIhytmJa
"""
def get_wpa_supplicant_value(interface, key):
@@ -51,9 +95,14 @@ def get_wpa_supplicant_value(interface, key):
tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
return tmp[0]
+def get_certificate_count(interface, cert_type):
+ tmp = read_file(f'/run/wpa_supplicant/{interface}_{cert_type}.pem')
+ return tmp.count(CERT_BEGIN)
+
class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
@@ -98,24 +147,6 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_commit()
- def test_dhcp_disable_interface(self):
- # When interface is configured as admin down, it must be admin down
- # even when dhcpc starts on the given interface
- for interface in self._interfaces:
- self.cli_set(self._base_path + [interface, 'disable'])
-
- # Also enable DHCP (ISC DHCP always places interface in admin up
- # state so we check that we do not start DHCP client.
- # https://phabricator.vyos.net/T2767
- self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
-
- self.cli_commit()
-
- # Validate interface state
- for interface in self._interfaces:
- flags = read_file(f'/sys/class/net/{interface}/flags')
- self.assertEqual(int(flags, 16) & 1, 0)
-
def test_offloading_rps(self):
# enable RPS on all available CPUs, RPS works woth a CPU bitmask,
# where each bit represents a CPU (core/thread). The formula below
@@ -165,16 +196,23 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_commit()
def test_eapol_support(self):
- ca_name = 'eapol'
- cert_name = 'eapol'
+ ca_certs = {
+ 'eapol-server-ca-root': server_ca_root_cert_data,
+ 'eapol-server-ca-intermediate': server_ca_intermediate_cert_data,
+ 'eapol-client-ca-root': client_ca_root_cert_data,
+ 'eapol-client-ca-intermediate': client_ca_intermediate_cert_data,
+ }
+ cert_name = 'eapol-client'
- self.cli_set(['pki', 'ca', ca_name, 'certificate', cert_data.replace('\n','')])
- self.cli_set(['pki', 'certificate', cert_name, 'certificate', cert_data.replace('\n','')])
- self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', key_data.replace('\n','')])
+ for name, data in ca_certs.items():
+ self.cli_set(['pki', 'ca', name, 'certificate', data.replace('\n','')])
+
+ self.cli_set(['pki', 'certificate', cert_name, 'certificate', client_cert_data.replace('\n','')])
+ self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', client_key_data.replace('\n','')])
for interface in self._interfaces:
# Enable EAPoL
- self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', ca_name])
+ self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-server-ca-intermediate'])
self.cli_set(self._base_path + [interface, 'eapol', 'certificate', cert_name])
self.cli_commit()
@@ -206,7 +244,12 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
tmp = get_wpa_supplicant_value(interface, 'identity')
self.assertEqual(f'"{mac}"', tmp)
- self.cli_delete(['pki', 'ca', ca_name])
+ # Check certificate files have the full chain
+ self.assertEqual(get_certificate_count(interface, 'ca'), 2)
+ self.assertEqual(get_certificate_count(interface, 'cert'), 3)
+
+ for name in ca_certs:
+ self.cli_delete(['pki', 'ca', name])
self.cli_delete(['pki', 'certificate', cert_name])
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index e4280a5b7..5b10bfa44 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -40,6 +40,7 @@ def get_cipher(interface):
class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._base_path = ['interfaces', 'macsec']
diff --git a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
index ae899cddd..adcadc5eb 100755
--- a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -23,6 +23,7 @@ from base_interfaces_test import BasicInterfaceTest
class PEthInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index fc2e254d6..99c25c374 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -44,14 +44,14 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
# call base-classes classmethod
super(cls, cls).setUpClass()
- def setUp(self):
- super().setUp()
- self.cli_set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32'])
- self.cli_set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128'])
+ # create some test interfaces
+ cls.cli_set(cls, ['interfaces', 'dummy', source_if, 'address', cls.local_v4 + '/32'])
+ cls.cli_set(cls, ['interfaces', 'dummy', source_if, 'address', cls.local_v6 + '/128'])
- def tearDown(self):
- self.cli_delete(['interfaces', 'dummy', source_if])
- super().tearDown()
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', source_if])
+ super().tearDownClass()
def test_ipv4_encapsulations(self):
# When running tests ensure that for certain encapsulation types the
@@ -202,7 +202,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(encapsulation, conf['linkinfo']['info_kind'])
self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
- self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
# Change remote ip address (inc host by 2
new_remote = inc_ip(remote_ip4, 2)
@@ -239,7 +239,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(encapsulation, conf['linkinfo']['info_kind'])
self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
- self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
self.assertEqual(int(idx), conf['linkinfo']['info_data']['erspan_index'])
@@ -295,7 +295,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(encapsulation, conf['linkinfo']['info_kind'])
self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
- self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
self.assertEqual(erspan_ver, conf['linkinfo']['info_data']['erspan_ver'])
@@ -312,5 +312,89 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
conf = get_interface_config(interface)
self.assertEqual(new_remote, conf['linkinfo']['info_data']['remote'])
+ def test_tunnel_src_any_gre_key(self):
+ interface = f'tun1280'
+ encapsulation = 'gre'
+ src_addr = '0.0.0.0'
+ key = '127'
+
+ self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation])
+ self.cli_set(self._base_path + [interface, 'source-address', src_addr])
+ # GRE key must be supplied with a 0.0.0.0 source address
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'key', key])
+
+ self.cli_commit()
+
+ def test_multiple_gre_tunnel_same_remote(self):
+ tunnels = {
+ 'tun10' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.4',
+ },
+ 'tun20' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.4',
+ },
+ }
+
+ for tunnel, tunnel_config in tunnels.items():
+ self.cli_set(self._base_path + [tunnel, 'encapsulation', tunnel_config['encapsulation']])
+ if 'source_interface' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'source-interface', tunnel_config['source_interface']])
+ if 'remote' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'remote', tunnel_config['remote']])
+
+ # GRE key must be supplied when two or more tunnels are formed to the same desitnation
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for tunnel, tunnel_config in tunnels.items():
+ self.cli_set(self._base_path + [tunnel, 'parameters', 'ip', 'key', tunnel.lstrip('tun')])
+
+ self.cli_commit()
+
+ for tunnel, tunnel_config in tunnels.items():
+ conf = get_interface_config(tunnel)
+ ip_key = tunnel.lstrip('tun')
+
+ self.assertEqual(tunnel_config['source_interface'], conf['link'])
+ self.assertEqual(tunnel_config['encapsulation'], conf['linkinfo']['info_kind'])
+ self.assertEqual(tunnel_config['remote'], conf['linkinfo']['info_data']['remote'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
+
+ def test_multiple_gre_tunnel_different_remote(self):
+ tunnels = {
+ 'tun10' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.4',
+ },
+ 'tun20' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.5',
+ },
+ }
+
+ for tunnel, tunnel_config in tunnels.items():
+ self.cli_set(self._base_path + [tunnel, 'encapsulation', tunnel_config['encapsulation']])
+ if 'source_interface' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'source-interface', tunnel_config['source_interface']])
+ if 'remote' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'remote', tunnel_config['remote']])
+
+ self.cli_commit()
+
+ for tunnel, tunnel_config in tunnels.items():
+ conf = get_interface_config(tunnel)
+
+ self.assertEqual(tunnel_config['source_interface'], conf['link'])
+ self.assertEqual(tunnel_config['encapsulation'], conf['linkinfo']['info_kind'])
+ self.assertEqual(tunnel_config['remote'], conf['linkinfo']['info_data']['remote'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index 9278adadd..f34b99ea4 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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,8 +18,9 @@ import unittest
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
+from vyos.util import get_bridge_fdb
from vyos.util import get_interface_config
-
+from vyos.template import is_ipv6
from base_interfaces_test import BasicInterfaceTest
class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
@@ -33,6 +34,8 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
'vxlan10': ['vni 10', 'remote 127.0.0.2'],
'vxlan20': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'],
'vxlan30': ['vni 30', 'remote 2001:db8:2000::1', 'source-address 2001:db8:1000::1', 'parameters ipv6 flowlabel 0x1000'],
+ 'vxlan40': ['vni 40', 'remote 127.0.0.2', 'remote 127.0.0.3'],
+ 'vxlan50': ['vni 50', 'remote 2001:db8:2000::1', 'remote 2001:db8:2000::2', 'parameters ipv6 flowlabel 0x1000'],
}
cls._interfaces = list(cls._options)
# call base-classes classmethod
@@ -55,21 +58,34 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
ttl = 20
for interface in self._interfaces:
options = get_interface_config(interface)
+ bridge = get_bridge_fdb(interface)
vni = options['linkinfo']['info_data']['id']
self.assertIn(f'vni {vni}', self._options[interface])
- if any('link' in s for s in self._options[interface]):
+ if any('source-interface' in s for s in self._options[interface]):
link = options['linkinfo']['info_data']['link']
self.assertIn(f'source-interface {link}', self._options[interface])
- if any('local6' in s for s in self._options[interface]):
- remote = options['linkinfo']['info_data']['local6']
- self.assertIn(f'source-address {local6}', self._options[interface])
-
- if any('remote6' in s for s in self._options[interface]):
- remote = options['linkinfo']['info_data']['remote6']
- self.assertIn(f'remote {remote}', self._options[interface])
+ # Verify source-address setting was properly configured on the Kernel
+ if any('source-address' in s for s in self._options[interface]):
+ for s in self._options[interface]:
+ if 'source-address' in s:
+ address = s.split()[-1]
+ if is_ipv6(address):
+ tmp = options['linkinfo']['info_data']['local6']
+ else:
+ tmp = options['linkinfo']['info_data']['local']
+ self.assertIn(f'source-address {tmp}', self._options[interface])
+
+ # Verify remote setting was properly configured on the Kernel
+ if any('remote' in s for s in self._options[interface]):
+ for s in self._options[interface]:
+ if 'remote' in s:
+ for fdb in bridge:
+ if 'mac' in fdb and fdb['mac'] == '00:00:00:00:00:00':
+ remote = fdb['dst']
+ self.assertIn(f'remote {remote}', self._options[interface])
if any('group' in s for s in self._options[interface]):
group = options['linkinfo']['info_data']['group']
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index 8afe0da26..6b7b49792 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -185,4 +185,4 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 5844e1ec1..b232a2241 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -665,6 +665,40 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, config)
+ def test_prefix_list_duplicates(self):
+ # FRR does not allow to specify the same profix list rule multiple times
+ #
+ # vyos(config)# ip prefix-list foo seq 10 permit 192.0.2.0/24
+ # vyos(config)# ip prefix-list foo seq 20 permit 192.0.2.0/24
+ # % Configuration failed.
+ # Error type: validation
+ # Error description: duplicated prefix list value: 192.0.2.0/24
+
+ # There is also a VyOS verify() function to test this
+
+ prefix = '100.64.0.0/10'
+ prefix_list = 'duplicates'
+ test_range = range(20, 25)
+ path = base_path + ['prefix-list', prefix_list]
+
+ for rule in test_range:
+ self.cli_set(path + ['rule', str(rule), 'action', 'permit'])
+ self.cli_set(path + ['rule', str(rule), 'prefix', prefix])
+
+ # Duplicate prefixes
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ for rule in test_range:
+ self.cli_set(path + ['rule', str(rule), 'le', str(rule)])
+
+ self.cli_commit()
+
+ config = self.getFRRconfig('ip prefix-list', end='')
+ for rule in test_range:
+ tmp = f'ip prefix-list {prefix_list} seq {rule} permit {prefix} le {rule}'
+ self.assertIn(tmp, config)
+
def test_route_map(self):
access_list = '50'
as_path_list = '100'
@@ -1030,7 +1064,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
tmp = f'match ipv6 address prefix-list {rule_config["match"]["ipv6-address-pfx"]}'
self.assertIn(tmp, config)
if 'ipv6-nexthop' in rule_config['match']:
- tmp = f'match ipv6 next-hop {rule_config["match"]["ipv6-nexthop"]}'
+ tmp = f'match ipv6 next-hop address {rule_config["match"]["ipv6-nexthop"]}'
self.assertIn(tmp, config)
if 'large-community' in rule_config['match']:
tmp = f'match large-community {rule_config["match"]["large-community"]}'
@@ -1135,18 +1169,13 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- # Check generated configuration
-
- # Expected values
original = """
50: from 203.0.113.1 lookup 23
50: from 203.0.113.2 lookup 23
"""
tmp = cmd('ip rule show prio 50')
- original = original.split()
- tmp = tmp.split()
- self.assertEqual(tmp, original)
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for fwmark
def test_fwmark_table_id(self):
@@ -1161,17 +1190,32 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- # Check generated configuration
-
- # Expected values
original = """
101: from all fwmark 0x18 lookup 154
"""
tmp = cmd('ip rule show prio 101')
- original = original.split()
- tmp = tmp.split()
- self.assertEqual(tmp, original)
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for destination
+ def test_destination_table_id(self):
+ path = base_path + ['local-route']
+
+ dst = '203.0.113.1'
+ rule = '102'
+ table = '154'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+
+ self.cli_commit()
+
+ original = """
+ 102: from all to 203.0.113.1 lookup 154
+ """
+ tmp = cmd('ip rule show prio 102')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources with fwmark
def test_fwmark_sources_table_id(self):
@@ -1188,18 +1232,301 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- # Check generated configuration
-
- # Expected values
original = """
100: from 203.0.113.11 fwmark 0x17 lookup 150
100: from 203.0.113.12 fwmark 0x17 lookup 150
"""
tmp = cmd('ip rule show prio 100')
- original = original.split()
- tmp = tmp.split()
- self.assertEqual(tmp, original)
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources with iif
+ def test_iif_sources_table_id(self):
+ path = base_path + ['local-route']
+
+ sources = ['203.0.113.11', '203.0.113.12']
+ iif = 'lo'
+ rule = '100'
+ table = '150'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'source', src])
+
+ self.cli_commit()
+
+ # Check generated configuration
+ # Expected values
+ original = """
+ 100: from 203.0.113.11 iif lo lookup 150
+ 100: from 203.0.113.12 iif lo lookup 150
+ """
+ tmp = cmd('ip rule show prio 100')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources and destinations with fwmark
+ def test_fwmark_sources_destination_table_id(self):
+ path = base_path + ['local-route']
+
+ sources = ['203.0.113.11', '203.0.113.12']
+ destinations = ['203.0.113.13', '203.0.113.15']
+ fwmk = '23'
+ rule = '103'
+ table = '150'
+ for src in sources:
+ for dst in destinations:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 103: from 203.0.113.11 to 203.0.113.13 fwmark 0x17 lookup 150
+ 103: from 203.0.113.11 to 203.0.113.15 fwmark 0x17 lookup 150
+ 103: from 203.0.113.12 to 203.0.113.13 fwmark 0x17 lookup 150
+ 103: from 203.0.113.12 to 203.0.113.15 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table ipv6 for some sources ipv6
+ def test_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:123::/48', '2001:db8:126::/48']
+ rule = '50'
+ table = '23'
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+
+ self.cli_commit()
+
+ original = """
+ 50: from 2001:db8:123::/48 lookup 23
+ 50: from 2001:db8:126::/48 lookup 23
+ """
+ tmp = cmd('ip -6 rule show prio 50')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for fwmark ipv6
+ def test_fwmark_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ fwmk = '24'
+ rule = '100'
+ table = '154'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 100: from all fwmark 0x18 lookup 154
+ """
+ tmp = cmd('ip -6 rule show prio 100')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for destination ipv6
+ def test_destination_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ dst = '2001:db8:1337::/126'
+ rule = '101'
+ table = '154'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+
+ self.cli_commit()
+
+ original = """
+ 101: from all to 2001:db8:1337::/126 lookup 154
+ """
+ tmp = cmd('ip -6 rule show prio 101')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources with fwmark ipv6
+ def test_fwmark_sources_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:1338::/126', '2001:db8:1339::/126']
+ fwmk = '23'
+ rule = '102'
+ table = '150'
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 102: from 2001:db8:1338::/126 fwmark 0x17 lookup 150
+ 102: from 2001:db8:1339::/126 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip -6 rule show prio 102')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources with iif ipv6
+ def test_iif_sources_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:1338::/126', '2001:db8:1339::/126']
+ iif = 'lo'
+ rule = '102'
+ table = '150'
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
+
+ self.cli_commit()
+
+ # Check generated configuration
+ # Expected values
+ original = """
+ 102: from 2001:db8:1338::/126 iif lo lookup 150
+ 102: from 2001:db8:1339::/126 iif lo lookup 150
+ """
+ tmp = cmd('ip -6 rule show prio 102')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources and destinations with fwmark ipv6
+ def test_fwmark_sources_destination_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:1338::/126', '2001:db8:1339::/56']
+ destinations = ['2001:db8:13::/48', '2001:db8:16::/48']
+ fwmk = '23'
+ rule = '103'
+ table = '150'
+ for src in sources:
+ for dst in destinations:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip -6 rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test delete table for sources and destination with fwmark ipv4/ipv6
+ def test_delete_ipv4_ipv6_table_id(self):
+ path = base_path + ['local-route']
+ path_v6 = base_path + ['local-route6']
+
+ sources = ['203.0.113.0/24', '203.0.114.5']
+ destinations = ['203.0.112.0/24', '203.0.116.5']
+ sources_v6 = ['2001:db8:1338::/126', '2001:db8:1339::/56']
+ destinations_v6 = ['2001:db8:13::/48', '2001:db8:16::/48']
+ fwmk = '23'
+ rule = '103'
+ table = '150'
+ for src in sources:
+ for dst in destinations:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ for src in sources_v6:
+ for dst in destinations_v6:
+ self.cli_set(path_v6 + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path_v6 + ['rule', rule, 'source', src])
+ self.cli_set(path_v6 + ['rule', rule, 'destination', dst])
+ self.cli_set(path_v6 + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 103: from 203.0.113.0/24 to 203.0.116.5 fwmark 0x17 lookup 150
+ 103: from 203.0.114.5 to 203.0.112.0/24 fwmark 0x17 lookup 150
+ 103: from 203.0.114.5 to 203.0.116.5 fwmark 0x17 lookup 150
+ 103: from 203.0.113.0/24 to 203.0.112.0/24 fwmark 0x17 lookup 150
+ """
+ original_v6 = """
+ 103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip rule show prio 103')
+ tmp_v6 = cmd('ip -6 rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+ self.assertEqual(sort_ip(tmp_v6), sort_ip(original_v6))
+
+ self.cli_delete(path)
+ self.cli_delete(path_v6)
+ self.cli_commit()
+
+ tmp = cmd('ip rule show prio 103')
+ tmp_v6 = cmd('ip -6 rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), [])
+ self.assertEqual(sort_ip(tmp_v6), [])
+
+ # Test multiple commits ipv4
+ def test_multiple_commit_ipv4_table_id(self):
+ path = base_path + ['local-route']
+
+ sources = ['192.0.2.1', '192.0.2.2']
+ destination = '203.0.113.25'
+ rule = '105'
+ table = '151'
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'source', src])
+
+ self.cli_commit()
+
+ original_first = """
+ 105: from 192.0.2.1 lookup 151
+ 105: from 192.0.2.2 lookup 151
+ """
+ tmp = cmd('ip rule show prio 105')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original_first))
+
+ # Create second commit with added destination
+ self.cli_set(path + ['rule', rule, 'destination', destination])
+ self.cli_commit()
+
+ original_second = """
+ 105: from 192.0.2.1 to 203.0.113.25 lookup 151
+ 105: from 192.0.2.2 to 203.0.113.25 lookup 151
+ """
+ tmp = cmd('ip rule show prio 105')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original_second))
+
+
+def sort_ip(output):
+ o = '\n'.join([' '.join(line.strip().split()) for line in output.strip().splitlines()])
+ o = o.splitlines()
+ o.sort()
+ return o
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index 70a234187..9035f0832 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -31,8 +31,9 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
self.cli_delete(['interfaces', 'ethernet', 'eth0'])
+ self.cli_delete(['protocols', 'static'])
self.cli_delete(['policy', 'route'])
- self.cli_delete(['policy', 'ipv6-route'])
+ self.cli_delete(['policy', 'route6'])
self.cli_commit()
def test_pbr_mark(self):
@@ -62,19 +63,27 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
self.assertTrue(matched)
def test_pbr_table(self):
- self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'port', '8888'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'syn'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'destination', 'port', '8888'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'set', 'table', table_id])
self.cli_set(['interfaces', 'ethernet', 'eth0', 'policy', 'route', 'smoketest'])
+ self.cli_set(['interfaces', 'ethernet', 'eth0', 'policy', 'route6', 'smoketest6'])
self.cli_commit()
mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
+ # IPv4
+
nftables_search = [
['iifname "eth0"', 'jump VYOS_PBR_smoketest'],
- ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'meta mark set ' + mark_hex]
+ ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'meta mark set ' + mark_hex]
]
nftables_output = cmd('sudo nft list table ip mangle')
@@ -87,6 +96,25 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
break
self.assertTrue(matched)
+ # IPv6
+
+ nftables6_search = [
+ ['iifname "eth0"', 'jump VYOS_PBR6_smoketest'],
+ ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'meta mark set ' + mark_hex]
+ ]
+
+ nftables6_output = cmd('sudo nft list table ip6 mangle')
+
+ for search in nftables6_search:
+ matched = False
+ for line in nftables6_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+ # IP rule fwmark -> table
+
ip_rule_search = [
['fwmark ' + hex(table_mark_offset - int(table_id)), 'lookup ' + table_id]
]
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index d7230baf4..f1db5350a 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -36,112 +36,118 @@ bfd_profile = 'foo-bar-baz'
neighbor_config = {
'192.0.2.1' : {
- 'bfd' : '',
- 'cap_dynamic' : '',
- 'cap_ext_next' : '',
- 'remote_as' : '100',
- 'adv_interv' : '400',
- 'passive' : '',
- 'password' : 'VyOS-Secure123',
- 'shutdown' : '',
- 'cap_over' : '',
- 'ttl_security' : '5',
- 'local_as' : '300',
- 'route_map_in' : route_map_in,
- 'route_map_out': route_map_out,
+ 'bfd' : '',
+ 'cap_dynamic' : '',
+ 'cap_ext_next' : '',
+ 'remote_as' : '100',
+ 'adv_interv' : '400',
+ 'passive' : '',
+ 'password' : 'VyOS-Secure123',
+ 'shutdown' : '',
+ 'cap_over' : '',
+ 'ttl_security' : '5',
+ 'local_as' : '300',
+ 'route_map_in' : route_map_in,
+ 'route_map_out' : route_map_out,
'no_send_comm_ext' : '',
- 'addpath_all' : '',
+ 'addpath_all' : '',
},
'192.0.2.2' : {
- 'bfd_profile' : bfd_profile,
- 'remote_as' : '200',
- 'shutdown' : '',
- 'no_cap_nego' : '',
- 'port' : '667',
- 'cap_strict' : '',
- 'advertise_map': route_map_in,
- 'non_exist_map': route_map_out,
- 'pfx_list_in' : prefix_list_in,
- 'pfx_list_out' : prefix_list_out,
+ 'bfd_profile' : bfd_profile,
+ 'remote_as' : '200',
+ 'shutdown' : '',
+ 'no_cap_nego' : '',
+ 'port' : '667',
+ 'cap_strict' : '',
+ 'advertise_map' : route_map_in,
+ 'non_exist_map' : route_map_out,
+ 'pfx_list_in' : prefix_list_in,
+ 'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
},
'192.0.2.3' : {
- 'advertise_map': route_map_in,
- 'description' : 'foo bar baz',
- 'remote_as' : '200',
- 'passive' : '',
- 'multi_hop' : '5',
- 'update_src' : 'lo',
- 'peer_group' : 'foo',
+ 'advertise_map' : route_map_in,
+ 'description' : 'foo bar baz',
+ 'remote_as' : '200',
+ 'passive' : '',
+ 'multi_hop' : '5',
+ 'update_src' : 'lo',
+ 'peer_group' : 'foo',
+ 'graceful_rst' : '',
},
'2001:db8::1' : {
- 'advertise_map': route_map_in,
- 'exist_map' : route_map_out,
- 'cap_dynamic' : '',
- 'cap_ext_next' : '',
- 'remote_as' : '123',
- 'adv_interv' : '400',
- 'passive' : '',
- 'password' : 'VyOS-Secure123',
- 'shutdown' : '',
- 'cap_over' : '',
- 'ttl_security' : '5',
- 'local_as' : '300',
- 'solo' : '',
- 'route_map_in' : route_map_in,
- 'route_map_out': route_map_out,
+ 'advertise_map' : route_map_in,
+ 'exist_map' : route_map_out,
+ 'cap_dynamic' : '',
+ 'cap_ext_next' : '',
+ 'remote_as' : '123',
+ 'adv_interv' : '400',
+ 'passive' : '',
+ 'password' : 'VyOS-Secure123',
+ 'shutdown' : '',
+ 'cap_over' : '',
+ 'ttl_security' : '5',
+ 'local_as' : '300',
+ 'solo' : '',
+ 'route_map_in' : route_map_in,
+ 'route_map_out' : route_map_out,
'no_send_comm_std' : '',
'addpath_per_as' : '',
- 'peer_group' : 'foo-bar',
+ 'peer_group' : 'foo-bar',
},
'2001:db8::2' : {
- 'remote_as' : '456',
- 'shutdown' : '',
- 'no_cap_nego' : '',
- 'port' : '667',
- 'cap_strict' : '',
- 'pfx_list_in' : prefix_list_in6,
- 'pfx_list_out' : prefix_list_out6,
+ 'remote_as' : '456',
+ 'shutdown' : '',
+ 'no_cap_nego' : '',
+ 'port' : '667',
+ 'cap_strict' : '',
+ 'pfx_list_in' : prefix_list_in6,
+ 'pfx_list_out' : prefix_list_out6,
'no_send_comm_ext' : '',
- 'peer_group' : 'foo-bar_baz',
+ 'peer_group' : 'foo-bar_baz',
+ 'graceful_rst_hlp' : ''
},
}
peer_group_config = {
'foo' : {
- 'advertise_map': route_map_in,
- 'exist_map' : route_map_out,
- 'bfd' : '',
- 'remote_as' : '100',
- 'passive' : '',
- 'password' : 'VyOS-Secure123',
- 'shutdown' : '',
- 'cap_over' : '',
- 'ttl_security': '5',
+ 'advertise_map' : route_map_in,
+ 'exist_map' : route_map_out,
+ 'bfd' : '',
+ 'remote_as' : '100',
+ 'passive' : '',
+ 'password' : 'VyOS-Secure123',
+ 'shutdown' : '',
+ 'cap_over' : '',
+ 'ttl_security' : '5',
+ },
+ 'bar' : {
+ 'remote_as' : '111',
+ 'graceful_rst_no' : ''
},
'foo-bar' : {
- 'advertise_map': route_map_in,
- 'description' : 'foo peer bar group',
- 'remote_as' : '200',
- 'shutdown' : '',
- 'no_cap_nego' : '',
- 'local_as' : '300',
- 'pfx_list_in' : prefix_list_in,
- 'pfx_list_out' : prefix_list_out,
+ 'advertise_map' : route_map_in,
+ 'description' : 'foo peer bar group',
+ 'remote_as' : '200',
+ 'shutdown' : '',
+ 'no_cap_nego' : '',
+ 'local_as' : '300',
+ 'pfx_list_in' : prefix_list_in,
+ 'pfx_list_out' : prefix_list_out,
'no_send_comm_ext' : '',
},
'foo-bar_baz' : {
- 'advertise_map': route_map_in,
- 'non_exist_map': route_map_out,
- 'bfd_profile' : bfd_profile,
- 'cap_dynamic' : '',
- 'cap_ext_next' : '',
- 'remote_as' : '200',
- 'passive' : '',
- 'multi_hop' : '5',
- 'update_src' : 'lo',
- 'route_map_in' : route_map_in,
- 'route_map_out': route_map_out,
+ 'advertise_map' : route_map_in,
+ 'non_exist_map' : route_map_out,
+ 'bfd_profile' : bfd_profile,
+ 'cap_dynamic' : '',
+ 'cap_ext_next' : '',
+ 'remote_as' : '200',
+ 'passive' : '',
+ 'multi_hop' : '5',
+ 'update_src' : 'lo',
+ 'route_map_in' : route_map_in,
+ 'route_map_out' : route_map_out,
},
}
@@ -239,6 +245,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'non_exist_map' in peer_config:
base = f'{base} non-exist-map {peer_config["non_exist_map"]}'
self.assertIn(base, frrconfig)
+ if 'graceful_rst' in peer_config:
+ self.assertIn(f' neighbor {peer} graceful-restart', frrconfig)
+ if 'graceful_rst_no' in peer_config:
+ self.assertIn(f' neighbor {peer} graceful-restart-disable', frrconfig)
+ if 'graceful_rst_hlp' in peer_config:
+ self.assertIn(f' neighbor {peer} graceful-restart-helper', frrconfig)
def test_bgp_01_simple(self):
router_id = '127.0.0.1'
@@ -274,6 +286,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer])
self.cli_set(base_path + ['parameters', 'fast-convergence'])
self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time])
+ self.cli_set(base_path + ['parameters', 'no-suppress-duplicates'])
self.cli_set(base_path + ['parameters', 'reject-as-sets'])
self.cli_set(base_path + ['parameters', 'shutdown'])
self.cli_set(base_path + ['parameters', 'suppress-fib-pending'])
@@ -305,6 +318,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' bgp shutdown', frrconfig)
self.assertIn(f' bgp suppress-fib-pending', frrconfig)
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
+ self.assertIn(f' no bgp suppress-duplicates', frrconfig)
afiv4_config = self.getFRRconfig(' address-family ipv4 unicast')
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
@@ -318,6 +332,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def test_bgp_02_neighbors(self):
# Test out individual neighbor configuration items, not all of them are
# also available to a peer-group!
+ self.cli_set(base_path + ['parameters', 'deterministic-med'])
+
for peer, peer_config in neighbor_config.items():
afi = 'ipv4-unicast'
if is_ipv6(peer):
@@ -378,6 +394,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all'])
if 'addpath_per_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as'])
+ if 'graceful_rst' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'enable'])
+ if 'graceful_rst_no' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'disable'])
+ if 'graceful_rst_hlp' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'restart-helper'])
# Conditional advertisement
if 'advertise_map' in peer_config:
@@ -460,6 +482,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all'])
if 'addpath_per_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as'])
+ if 'graceful_rst' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'enable'])
+ if 'graceful_rst_no' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'disable'])
+ if 'graceful_rst_hlp' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'restart-helper'])
# Conditional advertisement
if 'advertise_map' in config:
@@ -479,6 +507,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'peer_group' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'peer-group', peer_config['peer_group']])
+
# commit changes
self.cli_commit()
@@ -854,4 +883,4 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' exit-address-family', afi_config)
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2, failfast=True)
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index 7f51c7178..11c765793 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -35,6 +35,10 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
# call base-classes classmethod
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()
@@ -71,13 +75,13 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
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}')
+ tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
@@ -104,11 +108,11 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR isisd configuration
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f'router isis {domain}', tmp)
self.assertIn(f' net {net}', tmp)
- tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}')
+ tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}', daemon='isisd')
self.assertIn(f'router isis {domain} vrf {vrf}', tmp)
self.assertIn(f' net {net}', tmp)
@@ -124,22 +128,26 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
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])
+ self.cli_set(base_path + ['level', 'level-2'])
# 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)
+ frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra')
self.assertIn(zebra_route_map, frrconfig)
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(' is-type level-2-only', tmp)
+
# 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)
+ frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra')
self.assertNotIn(zebra_route_map, frrconfig)
self.cli_delete(['policy', 'route-map', route_map])
@@ -159,7 +167,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
for afi in ['ipv4', 'ipv6']:
@@ -172,6 +180,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
password = 'foo'
self.isis_base_config()
+ for interface in self._interfaces:
+ self.cli_set(base_path + ['interface', interface, 'password', 'plaintext-password', f'{password}-{interface}'])
self.cli_set(base_path + ['area-password', 'plaintext-password', password])
self.cli_set(base_path + ['area-password', 'md5', password])
@@ -192,11 +202,14 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' domain-password clear {password}', tmp)
self.assertIn(f' area-password clear {password}', tmp)
+ for interface in self._interfaces:
+ tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
+ self.assertIn(f' isis password clear {password}-{interface}', tmp)
def test_isis_06_spf_delay_bfd(self):
network = 'point-to-point'
@@ -237,12 +250,12 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' spf-delay-ietf init-delay {init_delay} short-delay {short_delay} long-delay {long_delay} holddown {holddown} time-to-learn {time_to_learn}', tmp)
for interface in self._interfaces:
- tmp = self.getFRRconfig(f'interface {interface}')
+ tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' isis network {network}', tmp)
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
index 13d38d01b..c6751cc42 100755
--- a/smoketest/scripts/cli/test_protocols_mpls.py
+++ b/smoketest/scripts/cli/test_protocols_mpls.py
@@ -81,7 +81,6 @@ class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
self.assertTrue(process_named_running(PROCESS_NAME))
def test_mpls_basic(self):
- self.debug = True
router_id = '1.2.3.4'
transport_ipv4_addr = '5.6.7.8'
interfaces = Section.interfaces('ethernet')
@@ -114,4 +113,4 @@ class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' interface {interface}', afiv4_config)
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index ee58b0fe2..e433d06d0 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -40,6 +40,10 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['policy', 'route-map', route_map])
@@ -368,6 +372,30 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
+ def test_ospf_13_export_list(self):
+ # Verify explort-list works on ospf-area
+ acl = '100'
+ seq = '10'
+ area = '0.0.0.10'
+ network = '10.0.0.0/8'
+
+
+ self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit'])
+ self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any'])
+ self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'destination', 'any'])
+ self.cli_set(base_path + ['area', area, 'network', network])
+ self.cli_set(base_path + ['area', area, 'export-list', acl])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf')
+ self.assertIn(f'router ospf', frrconfig)
+ self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # default
+ self.assertIn(f' network {network} area {area}', frrconfig)
+ self.assertIn(f' area {area} export-list {acl}', frrconfig)
+
if __name__ == '__main__':
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index 1327fd910..944190089 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -38,6 +38,10 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['policy', 'route-map', route_map])
@@ -70,7 +74,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {default_area} range {prefix}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
@@ -78,7 +82,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig)
for interface in interfaces:
- if_config = self.getFRRconfig(f'interface {interface}')
+ if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d')
self.assertIn(f'ipv6 ospf6 area {default_area}', if_config)
self.cli_delete(['policy', 'access-list6', acl_name])
@@ -99,7 +103,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' distance {dist_global}', frrconfig)
self.assertIn(f' distance ospf6 intra-area {dist_intra_area} inter-area {dist_inter_area} external {dist_external}', frrconfig)
@@ -119,7 +123,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig)
@@ -150,13 +154,13 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
cost = '100'
priority = '10'
for interface in interfaces:
- if_config = self.getFRRconfig(f'interface {interface}')
+ if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d')
self.assertIn(f'interface {interface}', if_config)
self.assertIn(f' ipv6 ospf6 bfd', if_config)
self.assertIn(f' ipv6 ospf6 bfd profile {bfd_profile}', if_config)
@@ -180,7 +184,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {area_stub} stub', frrconfig)
self.assertIn(f' area {area_stub_nosum} stub no-summary', frrconfig)
@@ -206,7 +210,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {area_nssa} nssa', frrconfig)
self.assertIn(f' area {area_nssa_nosum} nssa default-information-originate no-summary', frrconfig)
@@ -226,7 +230,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -235,7 +239,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -261,15 +265,15 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
- frrconfig = self.getFRRconfig(f'interface {vrf_iface} vrf {vrf}')
- self.assertIn(f'interface {vrf_iface} vrf {vrf}', frrconfig)
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon='ospf6d')
+ self.assertIn(f'interface {vrf_iface}', frrconfig)
self.assertIn(f' ipv6 ospf6 bfd', frrconfig)
- frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}')
+ frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}', daemon='ospf6d')
self.assertIn(f'router ospf6 vrf {vrf}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id_vrf}', frrconfig)
@@ -278,4 +282,4 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index 4c4eb5a7c..3ef9c76d8 100755
--- a/smoketest/scripts/cli/test_protocols_static.py
+++ b/smoketest/scripts/cli/test_protocols_static.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -52,9 +52,16 @@ routes = {
},
'blackhole' : { 'distance' : '90' },
},
- '100.64.0.0/10' : {
+ '100.64.0.0/16' : {
'blackhole' : { },
},
+ '100.65.0.0/16' : {
+ 'reject' : { 'distance' : '10', 'tag' : '200' },
+ },
+ '100.66.0.0/16' : {
+ 'blackhole' : { },
+ 'reject' : { 'distance' : '10', 'tag' : '200' },
+ },
'2001:db8:100::/40' : {
'next_hop' : {
'2001:db8::1' : { 'distance' : '10' },
@@ -74,6 +81,9 @@ routes = {
},
'blackhole' : { 'distance' : '250', 'tag' : '500' },
},
+ '2001:db8:300::/40' : {
+ 'reject' : { 'distance' : '250', 'tag' : '500' },
+ },
'2001:db8::/32' : {
'blackhole' : { 'distance' : '200', 'tag' : '600' },
},
@@ -82,9 +92,15 @@ routes = {
tables = ['80', '81', '82']
class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- # This is our "target" VRF when leaking routes:
- self.cli_set(['vrf', 'name', 'black', 'table', '43210'])
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+ cls.cli_set(cls, ['vrf', 'name', 'black', 'table', '43210'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['vrf'])
+ super(cls, cls).tearDownClass()
def tearDown(self):
for route, route_config in routes.items():
@@ -135,6 +151,20 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
if 'tag' in route_config['blackhole']:
self.cli_set(base + ['blackhole', 'tag', route_config['blackhole']['tag']])
+ if 'reject' in route_config:
+ self.cli_set(base + ['reject'])
+ if 'distance' in route_config['reject']:
+ self.cli_set(base + ['reject', 'distance', route_config['reject']['distance']])
+ if 'tag' in route_config['reject']:
+ self.cli_set(base + ['reject', 'tag', route_config['reject']['tag']])
+
+ if {'blackhole', 'reject'} <= set(route_config):
+ # Can not use blackhole and reject at the same time
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base + ['blackhole'])
+ self.cli_delete(base + ['reject'])
+
# commit changes
self.cli_commit()
@@ -177,6 +207,11 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
else:
self.assertIn(tmp, frrconfig)
+ if {'blackhole', 'reject'} <= set(route_config):
+ # Can not use blackhole and reject at the same time
+ # Config error validated above - skip this route
+ continue
+
if 'blackhole' in route_config:
tmp = f'{ip_ipv6} route {route} blackhole'
if 'tag' in route_config['blackhole']:
@@ -186,6 +221,15 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, frrconfig)
+ if 'reject' in route_config:
+ tmp = f'{ip_ipv6} route {route} reject'
+ if 'tag' in route_config['reject']:
+ tmp += ' tag ' + route_config['reject']['tag']
+ if 'distance' in route_config['reject']:
+ tmp += ' ' + route_config['reject']['distance']
+
+ self.assertIn(tmp, frrconfig)
+
def test_02_static_table(self):
for table in tables:
for route, route_config in routes.items():
@@ -389,11 +433,8 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, frrconfig)
- self.cli_delete(['vrf'])
-
def test_04_static_zebra_route_map(self):
# Implemented because of T3328
- self.debug = True
route_map = 'foo-static-in'
self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index 14666db15..9adb9c042 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -461,12 +461,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
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'port 647;', config)
+ self.assertIn(f'peer port 647;', 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)
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 8e69efd9c..9413d22d1 100755
--- a/smoketest/scripts/cli/test_service_https.py
+++ b/smoketest/scripts/cli/test_service_https.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -15,15 +15,39 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
+import urllib3
+
+from requests import request
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.util import read_file
from vyos.util import run
-base_path = ['service', 'https']
+urllib3.disable_warnings()
+base_path = ['service', 'https']
pki_base = ['pki']
-cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0='
-key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww'
+
+cert_data = """
+MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
+WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
+MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
+BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
+UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
+QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
+ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
++dm/LDnp7C0=
+"""
+
+key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
+2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
+u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+"""
class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
def setUp(self):
@@ -61,9 +85,13 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
ret = run('sudo /usr/sbin/nginx -t')
self.assertEqual(ret, 0)
+ nginx_config = read_file('/etc/nginx/sites-enabled/default')
+ self.assertIn(f'listen {address}:{port} ssl;', nginx_config)
+ self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config)
+
def test_certificate(self):
- self.cli_set(pki_base + ['certificate', 'test_https', 'certificate', cert_data])
- self.cli_set(pki_base + ['certificate', 'test_https', 'private', 'key', key_data])
+ self.cli_set(pki_base + ['certificate', 'test_https', 'certificate', cert_data.replace('\n','')])
+ self.cli_set(pki_base + ['certificate', 'test_https', 'private', 'key', key_data.replace('\n','')])
self.cli_set(base_path + ['certificates', 'certificate', 'test_https'])
@@ -72,5 +100,43 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
ret = run('sudo /usr/sbin/nginx -t')
self.assertEqual(ret, 0)
+ def test_api_auth(self):
+ vhost_id = 'example'
+ address = '127.0.0.1'
+ port = '443'
+ name = 'localhost'
+
+ self.cli_set(base_path + ['api', 'socket'])
+ key = 'MySuperSecretVyOS'
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+
+ test_path = base_path + ['virtual-host', vhost_id]
+ self.cli_set(test_path + ['listen-address', address])
+ self.cli_set(test_path + ['listen-port', port])
+ self.cli_set(test_path + ['server-name', name])
+
+ self.cli_commit()
+
+ nginx_config = read_file('/etc/nginx/sites-enabled/default')
+ self.assertIn(f'listen {address}:{port} ssl;', nginx_config)
+ self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config)
+
+ url = f'https://{address}/retrieve'
+ payload = {'data': '{"op": "showConfig", "path": []}', 'key': f'{key}'}
+ headers = {}
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ # Must get HTTP code 200 on success
+ self.assertEqual(r.status_code, 200)
+
+ payload_invalid = {'data': '{"op": "showConfig", "path": []}', 'key': 'invalid'}
+ r = request('POST', url, verify=False, headers=headers, data=payload_invalid)
+ # Must get HTTP code 401 on invalid key (Unauthorized)
+ self.assertEqual(r.status_code, 401)
+
+ payload_no_key = {'data': '{"op": "showConfig", "path": []}'}
+ r = request('POST', url, verify=False, headers=headers, data=payload_no_key)
+ # Must get HTTP code 401 on missing key (Unauthorized)
+ self.assertEqual(r.status_code, 401)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_lldp.py b/smoketest/scripts/cli/test_service_lldp.py
new file mode 100755
index 000000000..64fdd9d1b
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_lldp.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+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
+from vyos.version import get_version_data
+
+PROCESS_NAME = 'lldpd'
+LLDPD_CONF = '/etc/lldpd.d/01-vyos.conf'
+base_path = ['service', 'lldp']
+mgmt_if = 'dum83513'
+mgmt_addr = ['1.2.3.4', '1.2.3.5']
+
+class TestServiceLLDP(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(cls, cls).setUpClass()
+
+ # create a test interfaces
+ for addr in mgmt_addr:
+ cls.cli_set(cls, ['interfaces', 'dummy', mgmt_if, 'address', addr + '/32'])
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', mgmt_if])
+ super().tearDownClass()
+
+ def tearDown(self):
+ # service must be running after it was configured
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # delete/stop LLDP service
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # service is no longer allowed to run after it was removed
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_lldp_basic(self):
+ self.cli_set(base_path)
+ self.cli_commit()
+
+ config = read_file(LLDPD_CONF)
+ version_data = get_version_data()
+ version = version_data['version']
+ self.assertIn(f'configure system platform VyOS', config)
+ self.assertIn(f'configure system description "VyOS {version}"', config)
+
+ def test_02_lldp_mgmt_address(self):
+ for addr in mgmt_addr:
+ self.cli_set(base_path + ['management-address', addr])
+ self.cli_commit()
+
+ config = read_file(LLDPD_CONF)
+ self.assertIn(f'configure system ip management pattern {",".join(mgmt_addr)}', config)
+
+ def test_03_lldp_interfaces(self):
+ for interface in Section.interfaces('ethernet'):
+ if not '.' in interface:
+ self.cli_set(base_path + ['interface', interface])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+
+ interface_list = []
+ for interface in Section.interfaces('ethernet'):
+ if not '.' in interface:
+ interface_list.append(interface)
+ tmp = ','.join(interface_list)
+ self.assertIn(f'configure system interface pattern "{tmp}"', config)
+
+ def test_04_lldp_all_interfaces(self):
+ self.cli_set(base_path + ['interface', 'all'])
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+ self.assertIn(f'configure system interface pattern "*"', config)
+
+ def test_05_lldp_location(self):
+ interface = 'eth0'
+ elin = '1234567890'
+ self.cli_set(base_path + ['interface', interface, 'location', 'elin', elin])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+
+ self.assertIn(f'configure ports {interface} med location elin "{elin}"', config)
+ self.assertIn(f'configure system interface pattern "{interface}"', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_monitoring_telegraf.py b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
index b857926e2..09937513e 100755
--- a/smoketest/scripts/cli/test_service_monitoring_telegraf.py
+++ b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
@@ -54,7 +54,7 @@ class TestMonitoringTelegraf(VyOSUnitTestSHIM.TestCase):
# Check telegraf config
self.assertIn(f'organization = "{org}"', config)
- self.assertIn(token, config)
+ self.assertIn(f' token = "$INFLUX_TOKEN"', config)
self.assertIn(f'urls = ["{url}:{port}"]', config)
self.assertIn(f'bucket = "{bucket}"', config)
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index a54c03919..9ed263655 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -15,13 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import paramiko
import re
import unittest
+from pwd import getpwall
+
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
+from vyos.util import is_systemd_service_running
from vyos.util import process_named_running
from vyos.util import read_file
@@ -49,6 +53,9 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
cls.cli_delete(cls, base_path)
def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
# delete testing SSH config
self.cli_delete(base_path)
self.cli_commit()
@@ -57,6 +64,11 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
self.assertTrue(os.path.isfile(key_dsa))
self.assertTrue(os.path.isfile(key_ed25519))
+ # Established SSH connections remains running after service is stopped.
+ # We can not use process_named_running here - we rather need to check
+ # that the systemd service is no longer running
+ self.assertFalse(is_systemd_service_running(PROCESS_NAME))
+
def test_ssh_default(self):
# Check if SSH service runs with default settings - used for checking
# behavior of <defaultValue> in XML definition
@@ -69,9 +81,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
port = get_config_value('Port')[0]
self.assertEqual('22', port)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
def test_ssh_single_listen_address(self):
# Check if SSH service can be configured and runs
self.cli_set(base_path + ['port', '1234'])
@@ -108,9 +117,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
keepalive = get_config_value('ClientAliveInterval')[0]
self.assertTrue("100" in keepalive)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
def test_ssh_multiple_listen_addresses(self):
# Check if SSH service can be configured and runs with multiple
# listen ports and listen-addresses
@@ -135,9 +141,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
for address in addresses:
self.assertIn(address, tmp)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
def test_ssh_vrf(self):
# Check if SSH service can be bound to given VRF
port = '22'
@@ -157,9 +160,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value('Port')
self.assertIn(port, tmp)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
# Check for process in VRF
tmp = cmd(f'ip vrf pids {vrf}')
self.assertIn(PROCESS_NAME, tmp)
@@ -167,5 +167,51 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# delete VRF
self.cli_delete(['vrf', 'name', vrf])
+ def test_ssh_login(self):
+ # Perform SSH login and command execution with a predefined user. The
+ # result (output of uname -a) must match the output if the command is
+ # run natively.
+ #
+ # We also try to login as an invalid user - this is not allowed to work.
+
+ def ssh_send_cmd(command, username, password, host='localhost'):
+ """ SSH command execution helper """
+ # Try to login via SSH
+ ssh_client = paramiko.SSHClient()
+ ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+ ssh_client.connect(hostname='localhost', username=username, password=password)
+ _, stdout, stderr = ssh_client.exec_command(command)
+ output = stdout.read().decode().strip()
+ error = stderr.read().decode().strip()
+ ssh_client.close()
+ return output, error
+
+ test_user = 'ssh_test'
+ test_pass = 'v2i57DZs8idUwMN3VC92'
+ test_command = 'uname -a'
+
+ self.cli_set(base_path)
+ self.cli_set(['system', 'login', 'user', test_user, 'authentication', 'plaintext-password', test_pass])
+
+ # commit changes
+ self.cli_commit()
+
+ # Login with proper credentials
+ output, error = ssh_send_cmd(test_command, test_user, test_pass)
+ # verify login
+ self.assertFalse(error)
+ self.assertEqual(output, cmd(test_command))
+
+ # Login with invalid credentials
+ with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
+ output, error = ssh_send_cmd(test_command, 'invalid_user', 'invalid_password')
+
+ self.cli_delete(['system', 'login', 'user', test_user])
+ self.cli_commit()
+
+ # After deletion the test user is not allowed to remain in /etc/passwd
+ usernames = [x[0] for x in getpwall()]
+ self.assertNotIn(test_user, usernames)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_upnp.py b/smoketest/scripts/cli/test_service_upnp.py
new file mode 100755
index 000000000..c3e9b600f
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_upnp.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.template import ip_from_cidr
+from vyos.util import read_file
+from vyos.util import process_named_running
+
+UPNP_CONF = '/run/upnp/miniupnp.conf'
+DAEMON = 'miniupnpd'
+interface = 'eth0'
+base_path = ['service', 'upnp']
+address_base = ['interfaces', 'ethernet', interface, 'address']
+
+ipv4_addr = '100.64.0.1/24'
+ipv6_addr = '2001:db8::1/64'
+
+class TestServiceUPnP(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)
+
+ cls.cli_set(cls, address_base + [ipv4_addr])
+ cls.cli_set(cls, address_base + [ipv6_addr])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, address_base)
+ cls._session.commit()
+
+ super(cls, cls).tearDownClass()
+
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(DAEMON))
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Check for running process
+ self.assertFalse(process_named_running(DAEMON))
+
+ def test_ipv4_base(self):
+ self.cli_set(base_path + ['nat-pmp'])
+ self.cli_set(base_path + ['listen', interface])
+
+ # check validate() - WAN interface is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['wan-interface', interface])
+
+ self.cli_commit()
+
+ config = read_file(UPNP_CONF)
+ self.assertIn(f'ext_ifname={interface}', config)
+ self.assertIn(f'listening_ip={interface}', config)
+ self.assertIn(f'enable_natpmp=yes', config)
+ self.assertIn(f'enable_upnp=yes', config)
+
+ def test_ipv6_base(self):
+ v6_addr = ip_from_cidr(ipv6_addr)
+
+ self.cli_set(base_path + ['nat-pmp'])
+ self.cli_set(base_path + ['listen', interface])
+ self.cli_set(base_path + ['listen', v6_addr])
+
+ # check validate() - WAN interface is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['wan-interface', interface])
+
+ self.cli_commit()
+
+ config = read_file(UPNP_CONF)
+ self.assertIn(f'ext_ifname={interface}', config)
+ self.assertIn(f'listening_ip={interface}', config)
+ self.assertIn(f'ipv6_listening_ip={v6_addr}', config)
+ self.assertIn(f'enable_natpmp=yes', config)
+ self.assertIn(f'enable_upnp=yes', config)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py
index 8a1a03ce7..ebbd9fe55 100755
--- a/smoketest/scripts/cli/test_service_webproxy.py
+++ b/smoketest/scripts/cli/test_service_webproxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -30,11 +30,19 @@ listen_if = 'dum3632'
listen_ip = '192.0.2.1'
class TestServiceWebProxy(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32'])
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(cls, cls).setUpClass()
+ # create a test interfaces
+ cls.cli_set(cls, ['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', listen_if])
+ super().tearDownClass()
def tearDown(self):
- self.cli_delete(['interfaces', 'dummy', listen_if])
self.cli_delete(base_path)
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index 857df1be6..84f17bcb0 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -39,6 +39,9 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
cls.cli_delete(cls, base_path)
def tearDown(self):
+ # after service removal process must no longer run
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
self.cli_delete(base_path)
self.cli_commit()
@@ -213,9 +216,9 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
uacctd = read_file(uacctd_conf)
tmp = []
- tmp.append('memory')
for server, server_config in netflow_server.items():
tmp.append(f'nfprobe[nf_{server}]')
+ tmp.append('memory')
self.assertIn('plugins: ' + ','.join(tmp), uacctd)
for server, server_config in netflow_server.items():
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index 1325d4b39..c8aea9100 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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,12 +17,16 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.template import is_ipv4
from vyos.util import read_file
+from vyos.util import get_interface_config
+from vyos.validate import is_intf_addr_assigned
base_path = ['system', 'ipv6']
file_forwarding = '/proc/sys/net/ipv6/conf/all/forwarding'
-file_disable = '/etc/modprobe.d/vyos_disable_ipv6.conf'
+file_disable = '/proc/sys/net/ipv6/conf/all/disable_ipv6'
file_dad = '/proc/sys/net/ipv6/conf/all/accept_dad'
file_multipath = '/proc/sys/net/ipv6/fib_multipath_hash_policy'
@@ -41,15 +45,6 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(file_forwarding), '0')
- def test_system_ipv6_disable(self):
- # Do not assign any IPv6 address on interfaces, this requires a reboot
- # which can not be tested, but we can read the config file :)
- self.cli_set(base_path + ['disable'])
- self.cli_commit()
-
- # Verify configuration file
- self.assertEqual(read_file(file_disable), 'options ipv6 disable_ipv6=1')
-
def test_system_ipv6_strict_dad(self):
# This defaults to 1
self.assertEqual(read_file(file_dad), '1')
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 69a06eeac..1131b6f93 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -23,6 +23,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from distutils.version import LooseVersion
from platform import release as kernel_version
from subprocess import Popen, PIPE
+from pwd import getpwall
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
@@ -52,6 +53,11 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
+ # After deletion, a user is not allowed to remain in /etc/passwd
+ usernames = [x[0] for x in getpwall()]
+ for user in users:
+ self.assertNotIn(user, usernames)
+
def test_add_linux_system_user(self):
# We are not allowed to re-use a username already taken by the Linux
# base system
@@ -235,4 +241,4 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertTrue(tmp)
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_logs.py b/smoketest/scripts/cli/test_system_logs.py
index 0c11c4663..92fa9c3d9 100755
--- a/smoketest/scripts/cli/test_system_logs.py
+++ b/smoketest/scripts/cli/test_system_logs.py
@@ -114,4 +114,4 @@ class TestSystemLogs(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index e8cc64463..c8cf04b7d 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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,7 +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/>.
-import re
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
@@ -29,17 +28,14 @@ PROCESS_NAME = 'ntpd'
NTP_CONF = '/run/ntpd/ntpd.conf'
base_path = ['system', 'ntp']
-def get_config_value(key):
- tmp = read_file(NTP_CONF)
- tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
- # remove possible trailing whitespaces
- return [item.strip() for item in tmp]
-
class TestSystemNTP(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 :)
- self.cli_delete(base_path)
+ cls.cli_delete(cls, base_path)
def tearDown(self):
self.cli_delete(base_path)
@@ -47,35 +43,38 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.assertFalse(process_named_running(PROCESS_NAME))
- def test_ntp_options(self):
+ def test_01_ntp_options(self):
# Test basic NTP support with multiple servers and their options
servers = ['192.0.2.1', '192.0.2.2']
options = ['noselect', 'preempt', 'prefer']
- ntp_pool = 'pool.vyos.io'
+ pools = ['pool.vyos.io']
for server in servers:
for option in options:
self.cli_set(base_path + ['server', server, option])
# Test NTP pool
- self.cli_set(base_path + ['server', ntp_pool, 'pool'])
+ for pool in pools:
+ self.cli_set(base_path + ['server', pool, 'pool'])
# commit changes
self.cli_commit()
# Check generated configuration
- tmp = get_config_value('server')
- for server in servers:
- test = f'{server} iburst ' + ' '.join(options)
- self.assertTrue(test in tmp)
+ config = read_file(NTP_CONF)
+ self.assertIn('driftfile /var/lib/ntp/ntp.drift', config)
+ self.assertIn('restrict default noquery nopeer notrap nomodify', config)
+ self.assertIn('restrict source nomodify notrap noquery', config)
+ self.assertIn('restrict 127.0.0.1', config)
+ self.assertIn('restrict -6 ::1', config)
- tmp = get_config_value('pool')
- self.assertTrue(f'{ntp_pool} iburst' in tmp)
+ for server in servers:
+ self.assertIn(f'server {server} iburst ' + ' '.join(options), config)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ for pool in pools:
+ self.assertIn(f'pool {pool} iburst', config)
- def test_ntp_clients(self):
+ def test_02_ntp_clients(self):
# Test the allowed-networks statement
listen_address = ['127.0.0.1', '::1']
for listen in listen_address:
@@ -96,23 +95,18 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Check generated client address configuration
+ config = read_file(NTP_CONF)
+ self.assertIn('restrict default ignore', config)
+
for network in networks:
network_address = address_from_cidr(network)
network_netmask = netmask_from_cidr(network)
-
- tmp = get_config_value(f'restrict {network_address}')[0]
- test = f'mask {network_netmask} nomodify notrap nopeer'
- self.assertTrue(tmp in test)
+ self.assertIn(f'restrict {network_address} mask {network_netmask} nomodify notrap nopeer', config)
# Check listen address
- tmp = get_config_value('interface')
- test = ['ignore wildcard']
+ self.assertIn('interface ignore wildcard', config)
for listen in listen_address:
- test.append(f'listen {listen}')
- self.assertEqual(tmp, test)
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertIn(f'interface listen {listen}', config)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 1433c7329..1338fe81c 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -28,6 +28,7 @@ vti_path = ['interfaces', 'vti']
nhrp_path = ['protocols', 'nhrp']
base_path = ['vpn', 'ipsec']
+charon_file = '/etc/strongswan.d/charon.conf'
dhcp_waiting_file = '/tmp/ipsec_dhcp_waiting'
swanctl_file = '/etc/swanctl/swanctl.conf'
@@ -171,8 +172,13 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Site to site
local_address = '192.0.2.10'
priority = '20'
+ life_bytes = '100000'
+ life_packets = '2000000'
peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ self.cli_set(base_path + ['esp-group', esp_group, 'life-bytes', life_bytes])
+ self.cli_set(base_path + ['esp-group', esp_group, 'life-packets', life_packets])
+
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
self.cli_set(peer_base_path + ['ike-group', ike_group])
@@ -197,6 +203,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
swanctl_conf_lines = [
f'version = 2',
f'auth = psk',
+ f'life_bytes = {life_bytes}',
+ f'life_packets = {life_packets}',
f'rekey_time = 28800s', # default value
f'proposals = aes128-sha1-modp1024',
f'esp_proposals = aes128-sha1-modp1024',
@@ -237,6 +245,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
+ self.cli_set(peer_base_path + ['connection-type', 'none'])
self.cli_set(peer_base_path + ['ike-group', ike_group])
self.cli_set(peer_base_path + ['default-esp-group', esp_group])
self.cli_set(peer_base_path + ['local-address', local_address])
@@ -265,6 +274,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'mode = tunnel',
f'local_ts = 172.16.10.0/24,172.16.11.0/24',
f'remote_ts = 172.17.10.0/24,172.17.11.0/24',
+ f'start_action = none',
f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one
f'if_id_out = {if_id}',
f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
@@ -416,5 +426,75 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# There is only one VTI test so no need to delete this globally in tearDown()
self.cli_delete(vti_path)
+
+ def test_06_flex_vpn_vips(self):
+ local_address = '192.0.2.5'
+ local_id = 'vyos-r1'
+ remote_id = 'vyos-r2'
+ peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+
+ self.cli_set(tunnel_path + ['tun1', 'encapsulation', 'gre'])
+ self.cli_set(tunnel_path + ['tun1', 'source-address', local_address])
+
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['options', 'flexvpn'])
+ self.cli_set(base_path + ['options', 'interface', 'tun1'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+
+ self.cli_set(peer_base_path + ['authentication', 'id', local_id])
+ self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
+ self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
+ self.cli_set(peer_base_path + ['authentication', 'remote-id', remote_id])
+ self.cli_set(peer_base_path + ['connection-type', 'initiate'])
+ self.cli_set(peer_base_path + ['ike-group', ike_group])
+ self.cli_set(peer_base_path + ['default-esp-group', esp_group])
+ self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'gre'])
+
+ self.cli_set(peer_base_path + ['virtual-address', '203.0.113.55'])
+ self.cli_set(peer_base_path + ['virtual-address', '203.0.113.56'])
+
+ self.cli_commit()
+
+ # Verify strongSwan configuration
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_conf_lines = [
+ f'version = 2',
+ f'vips = 203.0.113.55, 203.0.113.56',
+ f'life_time = 3600s', # default value
+ f'local_addrs = {local_address} # dhcp:no',
+ f'remote_addrs = {peer_ip}',
+ f'peer_{peer_ip.replace(".","-")}_tunnel_1',
+ f'mode = tunnel',
+ ]
+
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_secrets_lines = [
+ f'id-local = {local_address} # dhcp:no',
+ f'id-remote = {peer_ip}',
+ f'id-localid = {local_id}',
+ f'id-remoteid = {remote_id}',
+ f'secret = "{secret}"',
+ ]
+
+ for line in swanctl_secrets_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Verify charon configuration
+ charon_conf = read_file(charon_file)
+ charon_conf_lines = [
+ f'# Cisco FlexVPN',
+ f'cisco_flexvpn = yes',
+ f'install_virtual_ip = yes',
+ f'install_virtual_ip_on = tun1',
+ ]
+
+ for line in charon_conf_lines:
+ self.assertIn(line, charon_conf)
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 5ffa9c086..c591d6cf5 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -25,9 +25,10 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
-from vyos.template import is_ipv6
+from vyos.template import is_ipv4
from vyos.util import cmd
from vyos.util import read_file
+from vyos.util import get_interface_config
from vyos.validate import is_intf_addr_assigned
base_path = ['vrf']
@@ -105,10 +106,13 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
+ tmp = get_interface_config(vrf)
+ self.assertEqual(int(table), tmp['linkinfo']['info_data']['table'])
+
# Increment table ID for the next run
table = str(int(table) + 1)
- def test_vrf_loopback_ips(self):
+ def test_vrf_loopbacks_ips(self):
table = '2000'
for vrf in vrfs:
base = base_path + ['name', vrf]
@@ -119,10 +123,13 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify VRF configuration
+ loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
- self.assertTrue(is_intf_addr_assigned(vrf, '127.0.0.1'))
- self.assertTrue(is_intf_addr_assigned(vrf, '::1'))
+ # Ensure VRF was created
+ self.assertIn(vrf, interfaces())
+ # Test for proper loopback IP assignment
+ for addr in loopbacks:
+ self.assertTrue(is_intf_addr_assigned(vrf, addr))
def test_vrf_bind_all(self):
table = '2000'
@@ -174,11 +181,11 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # Verify & cleanup
+ # Verify VRF assignmant
for interface in self._interfaces:
- # os.readlink resolves to: '../../../../../virtual/net/foovrf'
- tmp = os.readlink(f'/sys/class/net/{interface}/master').split('/')[-1]
- self.assertEqual(tmp, vrf)
+ tmp = get_interface_config(interface)
+ self.assertEqual(vrf, tmp['master'])
+
# cleanup
section = Section.section(interface)
self.cli_delete(['interfaces', section, interface, 'vrf'])
@@ -220,5 +227,45 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Increment table ID for the next run
table = str(int(table) + 1)
+ def test_vrf_link_local_ip_addresses(self):
+ # Testcase for issue T4331
+ table = '100'
+ vrf = 'orange'
+ interface = 'dum9998'
+ addresses = ['192.0.2.1/26', '2001:db8:9998::1/64', 'fe80::1/64']
+
+ for address in addresses:
+ self.cli_set(['interfaces', 'dummy', interface, 'address', address])
+
+ # Create dummy interfaces
+ self.cli_commit()
+
+ # ... and verify IP addresses got assigned
+ for address in addresses:
+ self.assertTrue(is_intf_addr_assigned(interface, address))
+
+ # Move interface to VRF
+ self.cli_set(base_path + ['name', vrf, 'table', table])
+ self.cli_set(['interfaces', 'dummy', interface, 'vrf', vrf])
+
+ # Apply VRF config
+ self.cli_commit()
+ # Ensure VRF got created
+ self.assertIn(vrf, interfaces())
+ # ... and IP addresses are still assigned
+ for address in addresses:
+ self.assertTrue(is_intf_addr_assigned(interface, address))
+ # Verify VRF table ID
+ tmp = get_interface_config(vrf)
+ self.assertEqual(int(table), tmp['linkinfo']['info_data']['table'])
+
+ # Verify interface is assigned to VRF
+ tmp = get_interface_config(interface)
+ self.assertEqual(vrf, tmp['master'])
+
+ # Delete Interface
+ self.cli_delete(['interfaces', 'dummy', interface])
+ self.cli_commit()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_zone_policy.py b/smoketest/scripts/cli/test_zone_policy.py
index c0af6164b..6e34f3179 100755
--- a/smoketest/scripts/cli/test_zone_policy.py
+++ b/smoketest/scripts/cli/test_zone_policy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -21,12 +21,18 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.util import cmd
class TestZonePolicy(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+ cls.cli_set(cls, ['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['firewall'])
+ super(cls, cls).tearDownClass()
def tearDown(self):
self.cli_delete(['zone-policy'])
- self.cli_delete(['firewall'])
self.cli_commit()
def test_basic_zone(self):
@@ -44,8 +50,8 @@ class TestZonePolicy(VyOSUnitTestSHIM.TestCase):
['oifname { "eth0" }', 'jump VZONE_smoketest-eth0'],
['jump VZONE_smoketest-local_IN'],
['jump VZONE_smoketest-local_OUT'],
- ['iifname { "eth0" }', 'jump smoketest'],
- ['oifname { "eth0" }', 'jump smoketest']
+ ['iifname { "eth0" }', 'jump NAME_smoketest'],
+ ['oifname { "eth0" }', 'jump NAME_smoketest']
]
nftables_output = cmd('sudo nft list table ip filter')
diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py
index c65ef9540..aabf2bdf5 100755
--- a/src/conf_mode/conntrack.py
+++ b/src/conf_mode/conntrack.py
@@ -35,6 +35,7 @@ airbag.enable()
conntrack_config = r'/etc/modprobe.d/vyatta_nf_conntrack.conf'
sysctl_file = r'/run/sysctl/10-vyos-conntrack.conf'
+nftables_ct_file = r'/run/nftables-ct.conf'
# Every ALG (Application Layer Gateway) consists of either a Kernel Object
# also called a Kernel Module/Driver or some rules present in iptables
@@ -81,16 +82,35 @@ def get_config(config=None):
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
default_values = defaults(base)
+ # XXX: T2665: we can not safely rely on the defaults() when there are
+ # tagNodes in place, it is better to blend in the defaults manually.
+ if 'timeout' in default_values and 'custom' in default_values['timeout']:
+ del default_values['timeout']['custom']
conntrack = dict_merge(default_values, conntrack)
return conntrack
def verify(conntrack):
+ if dict_search('ignore.rule', conntrack) != None:
+ for rule, rule_config in conntrack['ignore']['rule'].items():
+ if dict_search('destination.port', rule_config) or \
+ dict_search('source.port', rule_config):
+ if 'protocol' not in rule_config or rule_config['protocol'] not in ['tcp', 'udp']:
+ raise ConfigError(f'Port requires tcp or udp as protocol in rule {rule}')
+
return None
def generate(conntrack):
render(conntrack_config, 'conntrack/vyos_nf_conntrack.conf.tmpl', conntrack)
render(sysctl_file, 'conntrack/sysctl.conf.tmpl', conntrack)
+ render(nftables_ct_file, 'conntrack/nftables-ct.tmpl', conntrack)
+
+ # dry-run newly generated configuration
+ tmp = run(f'nft -c -f {nftables_ct_file}')
+ if tmp > 0:
+ if os.path.exists(nftables_ct_file):
+ os.unlink(nftables_ct_file)
+ raise ConfigError('Configuration file errors encountered!')
return None
@@ -127,6 +147,9 @@ def apply(conntrack):
if not find_nftables_ct_rule(rule):
cmd(f'nft insert rule ip raw VYOS_CT_HELPER {rule}')
+ # Load new nftables ruleset
+ cmd(f'nft -f {nftables_ct_file}')
+
if process_named_running('conntrackd'):
# Reload conntrack-sync daemon to fetch new sysctl values
resync_conntrackd()
diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py
index 8f9837c2b..34d1f7398 100755
--- a/src/conf_mode/conntrack_sync.py
+++ b/src/conf_mode/conntrack_sync.py
@@ -93,9 +93,9 @@ def verify(conntrack):
raise ConfigError('Can not configure expect-sync "all" with other protocols!')
if 'listen_address' in conntrack:
- address = conntrack['listen_address']
- if not is_addr_assigned(address):
- raise ConfigError(f'Specified listen-address {address} not assigned to any interface!')
+ for address in conntrack['listen_address']:
+ if not is_addr_assigned(address):
+ raise ConfigError(f'Specified listen-address {address} not assigned to any interface!')
vrrp_group = dict_search('failover_mechanism.vrrp.sync_group', conntrack)
if vrrp_group == None:
diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py
index 2e14e0b25..516671844 100755
--- a/src/conf_mode/containers.py
+++ b/src/conf_mode/containers.py
@@ -122,6 +122,18 @@ def verify(container):
raise ConfigError(f'IP address "{address}" can not be used for a container, '\
'reserved for the container engine!')
+ if 'device' in container_config:
+ for dev, dev_config in container_config['device'].items():
+ if 'source' not in dev_config:
+ raise ConfigError(f'Device "{dev}" has no source path configured!')
+
+ if 'destination' not in dev_config:
+ raise ConfigError(f'Device "{dev}" has no destination path configured!')
+
+ source = dev_config['source']
+ if not os.path.exists(source):
+ raise ConfigError(f'Device "{dev}" source path "{source}" does not exist!')
+
if 'environment' in container_config:
for var, cfg in container_config['environment'].items():
if 'value' not in cfg:
@@ -266,6 +278,14 @@ def apply(container):
c = c.replace('-', '_')
cap_add += f' --cap-add={c}'
+ # Add a host device to the container /dev/x:/dev/x
+ device = ''
+ if 'device' in container_config:
+ for dev, dev_config in container_config['device'].items():
+ source_dev = dev_config['source']
+ dest_dev = dev_config['destination']
+ device += f' --device={source_dev}:{dest_dev}'
+
# Check/set environment options "-e foo=bar"
env_opt = ''
if 'environment' in container_config:
@@ -296,9 +316,9 @@ def apply(container):
container_base_cmd = f'podman run --detach --interactive --tty --replace {cap_add} ' \
f'--memory {memory}m --memory-swap 0 --restart {restart} ' \
- f'--name {name} {port} {volume} {env_opt}'
+ f'--name {name} {device} {port} {volume} {env_opt}'
if 'allow_host_networks' in container_config:
- _cmd(f'{container_base_cmd} --net host {image}')
+ run(f'{container_base_cmd} --net host {image}')
else:
for network in container_config['network']:
ipparam = ''
@@ -306,19 +326,25 @@ def apply(container):
address = container_config['network'][network]['address']
ipparam = f'--ip {address}'
- counter = 0
- while True:
- if counter >= 10:
- break
- try:
- _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}')
- break
- except:
- counter = counter +1
- sleep(0.5)
+ run(f'{container_base_cmd} --net {network} {ipparam} {image}')
return None
+def run(container_cmd):
+ counter = 0
+ while True:
+ if counter >= 10:
+ break
+ try:
+ _cmd(container_cmd)
+ break
+ except:
+ counter = counter +1
+ sleep(0.5)
+
+ return None
+
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 23a16df63..fa9b21f20 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -16,6 +16,7 @@
import os
+from netifaces import interfaces
from sys import exit
from glob import glob
@@ -65,10 +66,6 @@ def get_config(config=None):
if conf.exists(base_nameservers):
dns.update({'system_name_server': conf.return_values(base_nameservers)})
- base_nameservers_dhcp = ['system', 'name-servers-dhcp']
- if conf.exists(base_nameservers_dhcp):
- dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)})
-
if 'authoritative_domain' in dns:
dns['authoritative_zones'] = []
dns['authoritative_zone_errors'] = []
@@ -272,9 +269,8 @@ def verify(dns):
raise ConfigError('Invalid authoritative records have been defined')
if 'system' in dns:
- if not ('system_name_server' in dns or 'system_name_server_dhcp' in dns):
- print("Warning: No 'system name-server' or 'system " \
- "name-servers-dhcp' configured")
+ if not 'system_name_server' in dns:
+ print('Warning: No "system name-server" configured')
return None
@@ -339,10 +335,15 @@ def apply(dns):
hc.delete_name_server_tags_recursor(['system'])
# add dhcp nameserver tags for configured interfaces
- if 'system_name_server_dhcp' in dns:
- for interface in dns['system_name_server_dhcp']:
- hc.add_name_server_tags_recursor(['dhcp-' + interface,
- 'dhcpv6-' + interface ])
+ if 'system_name_server' in dns:
+ for interface in dns['system_name_server']:
+ # system_name_server key contains both IP addresses and interface
+ # names (DHCP) to use DNS servers. We need to check if the
+ # value is an interface name - only if this is the case, add the
+ # interface based DNS forwarder.
+ if interface in interfaces():
+ hc.add_name_server_tags_recursor(['dhcp-' + interface,
+ 'dhcpv6-' + interface ])
# hostsd will generate the forward-zones file
# the list and keys() are required as get returns a dict, not list
diff --git a/src/conf_mode/firewall-interface.py b/src/conf_mode/firewall-interface.py
index b0df9dff4..9a5d278e9 100755
--- a/src/conf_mode/firewall-interface.py
+++ b/src/conf_mode/firewall-interface.py
@@ -31,6 +31,9 @@ from vyos import ConfigError
from vyos import airbag
airbag.enable()
+NAME_PREFIX = 'NAME_'
+NAME6_PREFIX = 'NAME6_'
+
NFT_CHAINS = {
'in': 'VYOS_FW_FORWARD',
'out': 'VYOS_FW_FORWARD',
@@ -127,7 +130,7 @@ def apply(if_firewall):
name = dict_search_args(if_firewall, direction, 'name')
if name:
- rule_exists = cleanup_rule('ip filter', chain, if_prefix, ifname, name)
+ rule_exists = cleanup_rule('ip filter', chain, if_prefix, ifname, f'{NAME_PREFIX}{name}')
if not rule_exists:
rule_action = 'insert'
@@ -138,24 +141,24 @@ def apply(if_firewall):
rule_action = 'add'
rule_prefix = f'position {handle}'
- run(f'nft {rule_action} rule ip filter {chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {name}')
+ run(f'nft {rule_action} rule ip filter {chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {NAME_PREFIX}{name}')
else:
cleanup_rule('ip filter', chain, if_prefix, ifname)
ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
if ipv6_name:
- rule_exists = cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname, ipv6_name)
+ rule_exists = cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname, f'{NAME6_PREFIX}{ipv6_name}')
if not rule_exists:
rule_action = 'insert'
rule_prefix = ''
- handle = state_policy_handle('ip filter', chain)
+ handle = state_policy_handle('ip6 filter', ipv6_chain)
if handle:
rule_action = 'add'
rule_prefix = f'position {handle}'
- run(f'nft {rule_action} rule ip6 filter {ipv6_chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {ipv6_name}')
+ run(f'nft {rule_action} rule ip6 filter {ipv6_chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {NAME6_PREFIX}{ipv6_name}')
else:
cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname)
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 75382034f..f33198a49 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import re
from glob import glob
from json import loads
@@ -22,6 +23,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configdict import node_changed
from vyos.configdiff import get_config_diff, Diff
from vyos.template import render
from vyos.util import cmd
@@ -33,7 +35,10 @@ from vyos import ConfigError
from vyos import airbag
airbag.enable()
+policy_route_conf_script = '/usr/libexec/vyos/conf_mode/policy-route.py'
+
nftables_conf = '/run/nftables.conf'
+nftables_defines_conf = '/run/nftables_defines.conf'
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'},
@@ -49,6 +54,9 @@ sysfs_config = {
'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337'}
}
+NAME_PREFIX = 'NAME_'
+NAME6_PREFIX = 'NAME6_'
+
preserve_chains = [
'INPUT',
'FORWARD',
@@ -65,6 +73,9 @@ preserve_chains = [
'VYOS_FRAG6_MARK'
]
+nft_iface_chains = ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL']
+nft6_iface_chains = ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
+
valid_groups = [
'address_group',
'network_group',
@@ -97,6 +108,35 @@ def get_firewall_interfaces(conf):
out.update(find_interfaces(iftype_conf))
return out
+def get_firewall_zones(conf):
+ used_v4 = []
+ used_v6 = []
+ zone_policy = conf.get_config_dict(['zone-policy'], key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ if 'zone' in zone_policy:
+ for zone, zone_conf in zone_policy['zone'].items():
+ if 'from' in zone_conf:
+ for from_zone, from_conf in zone_conf['from'].items():
+ name = dict_search_args(from_conf, 'firewall', 'name')
+ if name:
+ used_v4.append(name)
+
+ ipv6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name')
+ if ipv6_name:
+ used_v6.append(ipv6_name)
+
+ if 'intra_zone_filtering' in zone_conf:
+ name = dict_search_args(zone_conf, 'intra_zone_filtering', 'firewall', 'name')
+ if name:
+ used_v4.append(name)
+
+ ipv6_name = dict_search_args(zone_conf, 'intra_zone_filtering', 'firewall', 'ipv6_name')
+ if ipv6_name:
+ used_v6.append(ipv6_name)
+
+ return {'name': used_v4, 'ipv6_name': used_v6}
+
def get_config(config=None):
if config:
conf = config
@@ -104,16 +144,15 @@ def get_config(config=None):
conf = Config()
base = ['firewall']
- if not conf.exists(base):
- return {}
-
firewall = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
default_values = defaults(base)
firewall = dict_merge(default_values, firewall)
+ firewall['policy_resync'] = bool('group' in firewall or node_changed(conf, base + ['group']))
firewall['interfaces'] = get_firewall_interfaces(conf)
+ firewall['zone_policy'] = get_firewall_zones(conf)
if 'config_trap' in firewall and firewall['config_trap'] == 'enable':
diff = get_config_diff(conf)
@@ -121,6 +160,7 @@ def get_config(config=None):
firewall['trap_targets'] = conf.get_config_dict(['service', 'snmp', 'trap-target'],
key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
+
return firewall
def verify_rule(firewall, rule_conf, ipv6):
@@ -131,6 +171,12 @@ def verify_rule(firewall, rule_conf, ipv6):
if {'match_frag', 'match_non_frag'} <= set(rule_conf['fragment']):
raise ConfigError('Cannot specify both "match-frag" and "match-non-frag"')
+ if 'limit' in rule_conf:
+ if 'rate' in rule_conf['limit']:
+ rate_int = re.sub(r'\D', '', rule_conf['limit']['rate'])
+ if int(rate_int) < 1:
+ raise ConfigError('Limit rate integer cannot be less than 1')
+
if 'ipsec' in rule_conf:
if {'match_ipsec', 'match_non_ipsec'} <= set(rule_conf['ipsec']):
raise ConfigError('Cannot specify both "match-ipsec" and "match-non-ipsec"')
@@ -139,6 +185,23 @@ def verify_rule(firewall, rule_conf, ipv6):
if not {'count', 'time'} <= set(rule_conf['recent']):
raise ConfigError('Recent "count" and "time" values must be defined')
+ tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
+ if tcp_flags:
+ if dict_search_args(rule_conf, 'protocol') != 'tcp':
+ raise ConfigError('Protocol must be tcp when specifying tcp flags')
+
+ not_flags = dict_search_args(rule_conf, 'tcp', 'flags', 'not')
+ if not_flags:
+ duplicates = [flag for flag in tcp_flags if flag in not_flags]
+ if duplicates:
+ raise ConfigError(f'Cannot match a tcp flag as set and not set')
+
+ if 'protocol' in rule_conf:
+ if rule_conf['protocol'] == 'icmp' and ipv6:
+ raise ConfigError(f'Cannot match IPv4 ICMP protocol on IPv6, use ipv6-icmp')
+ if rule_conf['protocol'] == 'ipv6-icmp' and not ipv6:
+ raise ConfigError(f'Cannot match IPv6 ICMP protocol on IPv4, use icmp')
+
for side in ['destination', 'source']:
if side in rule_conf:
side_conf = rule_conf[side]
@@ -151,16 +214,19 @@ def verify_rule(firewall, rule_conf, ipv6):
if group in side_conf['group']:
group_name = side_conf['group'][group]
- fw_group = f'ipv6_{group}' if ipv6 and group in ['address_group', 'network_group'] else group
+ if group_name and group_name[0] == '!':
+ group_name = group_name[1:]
- if not dict_search_args(firewall, 'group', fw_group):
- error_group = fw_group.replace("_", "-")
- raise ConfigError(f'Group defined in rule but {error_group} is not configured')
+ fw_group = f'ipv6_{group}' if ipv6 and group in ['address_group', 'network_group'] else group
+ error_group = fw_group.replace("_", "-")
+ group_obj = dict_search_args(firewall, 'group', fw_group, group_name)
- if group_name not in firewall['group'][fw_group]:
- error_group = group.replace("_", "-")
+ if group_obj is None:
raise ConfigError(f'Invalid {error_group} "{group_name}" on firewall rule')
+ if not group_obj:
+ print(f'WARNING: {error_group} "{group_name}" has no members')
+
if 'port' in side_conf or dict_search_args(side_conf, 'group', 'port_group'):
if 'protocol' not in rule_conf:
raise ConfigError('Protocol must be defined if specifying a port or port-group')
@@ -169,10 +235,6 @@ def verify_rule(firewall, rule_conf, ipv6):
raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port or port-group')
def verify(firewall):
- # bail out early - looks like removal from running config
- if not firewall:
- return None
-
if 'config_trap' in firewall and firewall['config_trap'] == 'enable':
if not firewall['trap_targets']:
raise ConfigError(f'Firewall config-trap enabled but "service snmp trap-target" is not defined')
@@ -195,16 +257,34 @@ def verify(firewall):
name = dict_search_args(if_firewall, direction, 'name')
ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
- if name and not dict_search_args(firewall, 'name', name):
+ if name and dict_search_args(firewall, 'name', name) == None:
raise ConfigError(f'Firewall name "{name}" is still referenced on interface {ifname}')
- if ipv6_name and not dict_search_args(firewall, 'ipv6_name', ipv6_name):
+ if ipv6_name and dict_search_args(firewall, 'ipv6_name', ipv6_name) == None:
raise ConfigError(f'Firewall ipv6-name "{ipv6_name}" is still referenced on interface {ifname}')
+ for fw_name, used_names in firewall['zone_policy'].items():
+ for name in used_names:
+ if dict_search_args(firewall, fw_name, name) == None:
+ raise ConfigError(f'Firewall {fw_name.replace("_", "-")} "{name}" is still referenced in zone-policy')
+
return None
+def cleanup_rule(table, jump_chain):
+ commands = []
+ chains = nft_iface_chains if table == 'ip filter' else nft6_iface_chains
+ for chain in chains:
+ results = cmd(f'nft -a list chain {table} {chain}').split("\n")
+ for line in results:
+ if f'jump {jump_chain}' in line:
+ handle_search = re.search('handle (\d+)', line)
+ if handle_search:
+ commands.append(f'delete rule {table} {chain} handle {handle_search[1]}')
+ return commands
+
def cleanup_commands(firewall):
commands = []
+ commands_end = []
for table in ['ip filter', 'ip6 filter']:
state_chain = 'VYOS_STATE_POLICY' if table == 'ip filter' else 'VYOS_STATE_POLICY6'
json_str = cmd(f'nft -j list table {table}')
@@ -220,11 +300,12 @@ def cleanup_commands(firewall):
else:
commands.append(f'flush chain {table} {chain}')
elif chain not in preserve_chains and not chain.startswith("VZONE"):
- if table == 'ip filter' and dict_search_args(firewall, 'name', chain):
+ if table == 'ip filter' and dict_search_args(firewall, 'name', chain.replace(NAME_PREFIX, "", 1)) != None:
commands.append(f'flush chain {table} {chain}')
- elif table == 'ip6 filter' and dict_search_args(firewall, 'ipv6_name', chain):
+ elif table == 'ip6 filter' and dict_search_args(firewall, 'ipv6_name', chain.replace(NAME6_PREFIX, "", 1)) != None:
commands.append(f'flush chain {table} {chain}')
else:
+ commands += cleanup_rule(table, chain)
commands.append(f'delete chain {table} {chain}')
elif 'rule' in item:
rule = item['rule']
@@ -234,7 +315,10 @@ def cleanup_commands(firewall):
chain = rule['chain']
handle = rule['handle']
commands.append(f'delete rule {table} {chain} handle {handle}')
- return commands
+ elif 'set' in item:
+ set_name = item['set']['name']
+ commands_end.append(f'delete set {table} {set_name}')
+ return commands + commands_end
def generate(firewall):
if not os.path.exists(nftables_conf):
@@ -243,6 +327,7 @@ def generate(firewall):
firewall['cleanup_commands'] = cleanup_commands(firewall)
render(nftables_conf, 'firewall/nftables.tmpl', firewall)
+ render(nftables_defines_conf, 'firewall/nftables-defines.tmpl', firewall)
return None
def apply_sysfs(firewall):
@@ -306,6 +391,12 @@ def state_policy_rule_exists():
search_str = cmd(f'nft list chain ip filter VYOS_FW_FORWARD')
return 'VYOS_STATE_POLICY' in search_str
+def resync_policy_route():
+ # Update policy route as firewall groups were updated
+ tmp = run(policy_route_conf_script)
+ if tmp > 0:
+ print('Warning: Failed to re-apply policy route configuration')
+
def apply(firewall):
if 'first_install' in firewall:
run('nfct helper add rpc inet tcp')
@@ -325,6 +416,9 @@ def apply(firewall):
apply_sysfs(firewall)
+ if firewall['policy_resync']:
+ resync_policy_route()
+
post_apply_trap(firewall)
return None
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index 975f19acf..25bf54790 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -27,6 +27,7 @@ from vyos.configdict import dict_merge
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
from vyos.template import render
+from vyos.util import call
from vyos.util import cmd
from vyos.validate import is_addr_assigned
from vyos.xml import defaults
@@ -35,6 +36,8 @@ from vyos import airbag
airbag.enable()
uacctd_conf_path = '/run/pmacct/uacctd.conf'
+systemd_service = 'uacctd.service'
+systemd_override = f'/etc/systemd/system/{systemd_service}.d/override.conf'
nftables_nflog_table = 'raw'
nftables_nflog_chain = 'VYOS_CT_PREROUTING_HOOK'
egress_nftables_nflog_table = 'inet mangle'
@@ -236,7 +239,10 @@ def generate(flow_config):
if not flow_config:
return None
- render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', flow_config)
+ render(uacctd_conf_path, 'pmacct/uacctd.conf.tmpl', flow_config)
+ render(systemd_override, 'pmacct/override.conf.tmpl', flow_config)
+ # Reload systemd manager configuration
+ call('systemctl daemon-reload')
def apply(flow_config):
action = 'restart'
@@ -246,13 +252,13 @@ def apply(flow_config):
_nftables_config([], 'egress')
# Stop flow-accounting daemon and remove configuration file
- cmd('systemctl stop uacctd.service')
+ call(f'systemctl stop {systemd_service}')
if os.path.exists(uacctd_conf_path):
os.unlink(uacctd_conf_path)
return
# Start/reload flow-accounting daemon
- cmd(f'systemctl restart uacctd.service')
+ call(f'systemctl restart {systemd_service}')
# configure nftables rules for defined interfaces
if 'interface' in flow_config:
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index b5f5e919f..00f3d4f7f 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -66,6 +66,15 @@ def get_config(config=None):
if conf.exists('debug'):
http_api['debug'] = True
+ # this node is not available by CLI by default, and is reserved for
+ # the graphql tools. One can enable it for testing, with the warning
+ # that this will open an unauthenticated server. To do so
+ # mkdir /opt/vyatta/share/vyatta-cfg/templates/service/https/api/gql
+ # touch /opt/vyatta/share/vyatta-cfg/templates/service/https/api/gql/node.def
+ # and configure; editing the config alone is insufficient.
+ if conf.exists('gql'):
+ http_api['gql'] = True
+
if conf.exists('socket'):
http_api['socket'] = True
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 431d65f1f..ad5a0f499 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -27,8 +27,9 @@ from vyos.configdict import is_source_interface
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_dhcpv6
-from vyos.configverify import verify_source_interface
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_source_interface
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ifconfig import BondIf
@@ -132,10 +133,10 @@ def verify(bond):
return None
if 'arp_monitor' in bond:
- if 'target' in bond['arp_monitor'] and len(int(bond['arp_monitor']['target'])) > 16:
+ if 'target' in bond['arp_monitor'] and len(bond['arp_monitor']['target']) > 16:
raise ConfigError('The maximum number of arp-monitor targets is 16')
- if 'interval' in bond['arp_monitor'] and len(int(bond['arp_monitor']['interval'])) > 0:
+ if 'interval' in bond['arp_monitor'] and int(bond['arp_monitor']['interval']) > 0:
if bond['mode'] in ['802.3ad', 'balance-tlb', 'balance-alb']:
raise ConfigError('ARP link monitoring does not work for mode 802.3ad, ' \
'transmit-load-balance or adaptive-load-balance')
@@ -149,6 +150,7 @@ def verify(bond):
verify_address(bond)
verify_dhcpv6(bond)
verify_vrf(bond)
+ verify_mirror_redirect(bond)
# use common function to verify VLAN configuration
verify_vlan_config(bond)
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 4d3ebc587..b1f7e6d7c 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -22,12 +22,12 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import node_changed
-from vyos.configdict import leaf_node_changed
from vyos.configdict import is_member
from vyos.configdict import is_source_interface
from vyos.configdict import has_vlan_subinterface_configured
from vyos.configdict import dict_merge
from vyos.configverify import verify_dhcpv6
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_vrf
from vyos.ifconfig import BridgeIf
from vyos.validate import has_address_configured
@@ -106,6 +106,7 @@ def verify(bridge):
verify_dhcpv6(bridge)
verify_vrf(bridge)
+ verify_mirror_redirect(bridge)
ifname = bridge['ifname']
diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py
index 55c783f38..4a1eb7b93 100755
--- a/src/conf_mode/interfaces-dummy.py
+++ b/src/conf_mode/interfaces-dummy.py
@@ -21,6 +21,7 @@ from vyos.configdict import get_interface_dict
from vyos.configverify import verify_vrf
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import DummyIf
from vyos import ConfigError
from vyos import airbag
@@ -46,6 +47,7 @@ def verify(dummy):
verify_vrf(dummy)
verify_address(dummy)
+ verify_mirror_redirect(dummy)
return None
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index e7250fb49..6aea7a80e 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -25,14 +25,16 @@ from vyos.configverify import verify_address
from vyos.configverify import verify_dhcpv6
from vyos.configverify import verify_eapol
from vyos.configverify import verify_interface_exists
-from vyos.configverify import verify_mirror
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_mtu
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ethtool import Ethtool
from vyos.ifconfig import EthernetIf
-from vyos.pki import wrap_certificate
+from vyos.pki import find_chain
+from vyos.pki import encode_certificate
+from vyos.pki import load_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
from vyos.util import call
@@ -81,7 +83,7 @@ def verify(ethernet):
verify_address(ethernet)
verify_vrf(ethernet)
verify_eapol(ethernet)
- verify_mirror(ethernet)
+ verify_mirror_redirect(ethernet)
ethtool = Ethtool(ifname)
# No need to check speed and duplex keys as both have default values.
@@ -159,16 +161,26 @@ def generate(ethernet):
cert_name = ethernet['eapol']['certificate']
pki_cert = ethernet['pki']['certificate'][cert_name]
- write_file(cert_file_path, wrap_certificate(pki_cert['certificate']))
+ loaded_pki_cert = load_certificate(pki_cert['certificate'])
+ loaded_ca_certs = {load_certificate(c['certificate'])
+ for c in ethernet['pki']['ca'].values()}
+
+ cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs)
+
+ write_file(cert_file_path,
+ '\n'.join(encode_certificate(c) for c in cert_full_chain))
write_file(cert_key_path, wrap_private_key(pki_cert['private']['key']))
if 'ca_certificate' in ethernet['eapol']:
ca_cert_file_path = os.path.join(cfg_dir, f'{ifname}_ca.pem')
ca_cert_name = ethernet['eapol']['ca_certificate']
- pki_ca_cert = ethernet['pki']['ca'][cert_name]
+ pki_ca_cert = ethernet['pki']['ca'][ca_cert_name]
+
+ loaded_ca_cert = load_certificate(pki_ca_cert['certificate'])
+ ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
write_file(ca_cert_file_path,
- wrap_certificate(pki_ca_cert['certificate']))
+ '\n'.join(encode_certificate(c) for c in ca_full_chain))
else:
# delete configuration on interface removal
if os.path.isfile(wpa_suppl_conf.format(**ethernet)):
diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py
index 2a63b60aa..3a668226b 100755
--- a/src/conf_mode/interfaces-geneve.py
+++ b/src/conf_mode/interfaces-geneve.py
@@ -24,6 +24,7 @@ from vyos.configdict import get_interface_dict
from vyos.configverify import verify_address
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_bridge_delete
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import GeneveIf
from vyos import ConfigError
@@ -50,6 +51,7 @@ def verify(geneve):
verify_mtu_ipv6(geneve)
verify_address(geneve)
+ verify_mirror_redirect(geneve)
if 'remote' not in geneve:
raise ConfigError('Remote side must be configured')
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 9b6ddd5aa..22256bf4f 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -25,6 +25,7 @@ from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import L2TPv3If
from vyos.util import check_kmod
from vyos.validate import is_addr_assigned
@@ -76,6 +77,7 @@ def verify(l2tpv3):
verify_mtu_ipv6(l2tpv3)
verify_address(l2tpv3)
+ verify_mirror_redirect(l2tpv3)
return None
def generate(l2tpv3):
diff --git a/src/conf_mode/interfaces-loopback.py b/src/conf_mode/interfaces-loopback.py
index 193334443..e4bc15bb5 100755
--- a/src/conf_mode/interfaces-loopback.py
+++ b/src/conf_mode/interfaces-loopback.py
@@ -20,6 +20,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import LoopbackIf
from vyos import ConfigError
from vyos import airbag
@@ -39,6 +40,7 @@ def get_config(config=None):
return loopback
def verify(loopback):
+ verify_mirror_redirect(loopback)
return None
def generate(loopback):
diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py
index eab69f36e..96fc1c41c 100755
--- a/src/conf_mode/interfaces-macsec.py
+++ b/src/conf_mode/interfaces-macsec.py
@@ -29,6 +29,7 @@ from vyos.configverify import verify_vrf
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_source_interface
from vyos import ConfigError
from vyos import airbag
@@ -66,6 +67,7 @@ def verify(macsec):
verify_vrf(macsec)
verify_mtu_ipv6(macsec)
verify_address(macsec)
+ verify_mirror_redirect(macsec)
if not (('security' in macsec) and
('cipher' in macsec['security'])):
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 3b8fae710..83d1c6d9b 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -32,8 +32,10 @@ from shutil import rmtree
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_bridge_delete
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import VTunIf
from vyos.pki import load_dh_parameters
from vyos.pki import load_private_key
@@ -47,6 +49,7 @@ from vyos.template import is_ipv4
from vyos.template import is_ipv6
from vyos.util import call
from vyos.util import chown
+from vyos.util import cmd
from vyos.util import dict_search
from vyos.util import dict_search_args
from vyos.util import makedir
@@ -87,6 +90,9 @@ def get_config(config=None):
if 'deleted' not in openvpn:
openvpn['pki'] = tmp_pki
+ tmp = leaf_node_changed(conf, ['openvpn-option'])
+ if tmp: openvpn['restart_required'] = ''
+
# We have to get the dict using 'get_config_dict' instead of 'get_interface_dict'
# as 'get_interface_dict' merges the defaults in, so we can not check for defaults in there.
tmp = conf.get_config_dict(base + [openvpn['ifname']], get_first_key=True)
@@ -225,11 +231,12 @@ def verify(openvpn):
if 'local_address' not in openvpn and 'is_bridge_member' not in openvpn:
raise ConfigError('Must specify "local-address" or add interface to bridge')
- if len([addr for addr in openvpn['local_address'] if is_ipv4(addr)]) > 1:
- raise ConfigError('Only one IPv4 local-address can be specified')
+ if 'local_address' in openvpn:
+ if len([addr for addr in openvpn['local_address'] if is_ipv4(addr)]) > 1:
+ raise ConfigError('Only one IPv4 local-address can be specified')
- if len([addr for addr in openvpn['local_address'] if is_ipv6(addr)]) > 1:
- raise ConfigError('Only one IPv6 local-address can be specified')
+ if len([addr for addr in openvpn['local_address'] if is_ipv6(addr)]) > 1:
+ raise ConfigError('Only one IPv6 local-address can be specified')
if openvpn['device_type'] == 'tun':
if 'remote_address' not in openvpn:
@@ -268,7 +275,7 @@ def verify(openvpn):
if dict_search('remote_host', openvpn) in dict_search('remote_address', openvpn):
raise ConfigError('"remote-address" and "remote-host" can not be the same')
- if openvpn['device_type'] == 'tap':
+ if openvpn['device_type'] == 'tap' and 'local_address' in openvpn:
# we can only have one local_address, this is ensured above
v4addr = None
for laddr in openvpn['local_address']:
@@ -423,8 +430,8 @@ def verify(openvpn):
# verify specified IP address is present on any interface on this system
if 'local_host' in openvpn:
if not is_addr_assigned(openvpn['local_host']):
- raise ConfigError('local-host IP address "{local_host}" not assigned' \
- ' to any interface'.format(**openvpn))
+ print('local-host IP address "{local_host}" not assigned' \
+ ' to any interface'.format(**openvpn))
# TCP active
if openvpn['protocol'] == 'tcp-active':
@@ -489,6 +496,7 @@ def verify(openvpn):
raise ConfigError('Username for authentication is missing')
verify_vrf(openvpn)
+ verify_mirror_redirect(openvpn)
return None
@@ -647,9 +655,19 @@ def apply(openvpn):
return None
+ # verify specified IP address is present on any interface on this system
+ # Allow to bind service to nonlocal address, if it virtaual-vrrp address
+ # or if address will be assign later
+ if 'local_host' in openvpn:
+ if not is_addr_assigned(openvpn['local_host']):
+ cmd('sysctl -w net.ipv4.ip_nonlocal_bind=1')
+
# No matching OpenVPN process running - maybe it got killed or none
# existed - nevertheless, spawn new OpenVPN process
- call(f'systemctl reload-or-restart openvpn@{interface}.service')
+ action = 'reload-or-restart'
+ if 'restart_required' in openvpn:
+ action = 'restart'
+ call(f'systemctl {action} openvpn@{interface}.service')
o = VTunIf(**openvpn)
o.update(openvpn)
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index 584adc75e..bfb1fadd5 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -28,6 +28,7 @@ from vyos.configverify import verify_source_interface
from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_vrf
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import PPPoEIf
from vyos.template import render
from vyos.util import call
@@ -85,6 +86,7 @@ def verify(pppoe):
verify_authentication(pppoe)
verify_vrf(pppoe)
verify_mtu_ipv6(pppoe)
+ verify_mirror_redirect(pppoe)
if {'connect_on_demand', 'vrf'} <= set(pppoe):
raise ConfigError('On-demand dialing and VRF can not be used at the same time')
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index 945a2ea9c..f2c85554f 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -25,6 +25,7 @@ from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_source_interface
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_mtu_parent
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import MACVLANIf
from vyos import ConfigError
@@ -60,6 +61,7 @@ def verify(peth):
verify_vrf(peth)
verify_address(peth)
verify_mtu_parent(peth, peth['parent'])
+ verify_mirror_redirect(peth)
# use common function to verify VLAN configuration
verify_vlan_config(peth)
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index 30f57ec0c..f4668d976 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 yOS 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,24 +18,20 @@ 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
from vyos.configdict import get_interface_dict
-from vyos.configdict import node_changed
from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_mirror_redirect
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
from vyos.util import get_interface_config
from vyos.util import dict_search
from vyos import ConfigError
@@ -54,8 +50,24 @@ def get_config(config=None):
base = ['interfaces', 'tunnel']
tunnel = get_interface_dict(conf, base)
- tmp = leaf_node_changed(conf, ['encapsulation'])
- if tmp: tunnel.update({'encapsulation_changed': {}})
+ if 'deleted' not in tunnel:
+ tmp = leaf_node_changed(conf, ['encapsulation'])
+ if tmp: tunnel.update({'encapsulation_changed': {}})
+
+ # We also need to inspect other configured tunnels as there are Kernel
+ # restrictions where we need to comply. E.g. GRE tunnel key can't be used
+ # twice, or with multiple GRE tunnels to the same location we must specify
+ # a GRE key
+ conf.set_level(base)
+ tunnel['other_tunnels'] = conf.get_config_dict([], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+ # delete our own instance from this dict
+ ifname = tunnel['ifname']
+ del tunnel['other_tunnels'][ifname]
+ # if only one tunnel is present on the system, no need to keep this key
+ if len(tunnel['other_tunnels']) == 0:
+ del tunnel['other_tunnels']
# We must check if our interface is configured to be a DMVPN member
nhrp_base = ['protocols', 'nhrp', 'tunnel']
@@ -96,35 +108,47 @@ def verify(tunnel):
if 'direction' not in tunnel['parameters']['erspan']:
raise ConfigError('ERSPAN version 2 requires direction to be set!')
- # If tunnel source address any and key not set
+ # If tunnel source is any and gre key is not set
+ interface = tunnel['ifname']
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}" ' \
+ raise ConfigError(f'"parameters ip key" must be set for {interface} when '\
+ 'encapsulation is GRE!')
+
+ gre_encapsulations = ['gre', 'gretap']
+ if tunnel['encapsulation'] in gre_encapsulations and 'other_tunnels' in tunnel:
+ # Check pairs tunnel source-address/encapsulation/key with exists tunnels.
+ # Prevent the same key for 2 tunnels with same source-address/encap. T2920
+ for o_tunnel, o_tunnel_conf in tunnel['other_tunnels'].items():
+ # no match on encapsulation - bail out
+ our_encapsulation = tunnel['encapsulation']
+ their_encapsulation = o_tunnel_conf['encapsulation']
+ if our_encapsulation in gre_encapsulations and their_encapsulation \
+ not in gre_encapsulations:
+ continue
+
+ our_address = dict_search('source_address', tunnel)
+ our_key = dict_search('parameters.ip.key', tunnel)
+ their_address = dict_search('source_address', o_tunnel_conf)
+ their_key = dict_search('parameters.ip.key', o_tunnel_conf)
+ if our_key != None:
+ if their_address == our_address and their_key == our_key:
+ raise ConfigError(f'Key "{our_key}" for source-address "{our_address}" ' \
f'is already used for tunnel "{tunnel_if}"!')
+ else:
+ our_source_if = dict_search('source_interface', tunnel)
+ their_source_if = dict_search('source_interface', o_tunnel_conf)
+ our_remote = dict_search('remote', tunnel)
+ their_remote = dict_search('remote', o_tunnel_conf)
+ # If no IP GRE key is defined we can not have more then one GRE tunnel
+ # bound to any one interface/IP address and the same remote. This will
+ # result in a OS PermissionError: add tunnel "gre0" failed: File exists
+ if (their_address == our_address or our_source_if == their_source_if) and \
+ our_remote == their_remote:
+ raise ConfigError(f'Missing required "ip key" parameter when '\
+ 'running more then one GRE based tunnel on the '\
+ 'same source-interface/source-address')
# Keys are not allowed with ipip and sit tunnels
if tunnel['encapsulation'] in ['ipip', 'sit']:
@@ -134,6 +158,7 @@ def verify(tunnel):
verify_mtu_ipv6(tunnel)
verify_address(tunnel)
verify_vrf(tunnel)
+ verify_mirror_redirect(tunnel)
if 'source_interface' in tunnel:
verify_interface_exists(tunnel['source_interface'])
diff --git a/src/conf_mode/interfaces-vti.py b/src/conf_mode/interfaces-vti.py
index 57950ffea..f06fdff1b 100755
--- a/src/conf_mode/interfaces-vti.py
+++ b/src/conf_mode/interfaces-vti.py
@@ -19,6 +19,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import VTIIf
from vyos.util import dict_search
from vyos import ConfigError
@@ -39,6 +40,7 @@ def get_config(config=None):
return vti
def verify(vti):
+ verify_mirror_redirect(vti)
return None
def generate(vti):
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 1f097c4e3..0a9b51cac 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -21,9 +21,11 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_source_interface
from vyos.ifconfig import Interface
from vyos.ifconfig import VXLANIf
@@ -34,8 +36,8 @@ airbag.enable()
def get_config(config=None):
"""
- Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
- interface name will be added or a deleted flag
+ Retrive CLI config as dictionary. Dictionary can never be empty, as at least
+ the interface name will be added or a deleted flag
"""
if config:
conf = config
@@ -44,6 +46,16 @@ def get_config(config=None):
base = ['interfaces', 'vxlan']
vxlan = get_interface_dict(conf, base)
+ # VXLAN interfaces are picky and require recreation if certain parameters
+ # change. But a VXLAN interface should - of course - not be re-created if
+ # it's description or IP address is adjusted. Feels somehow logic doesn't it?
+ for cli_option in ['external', 'gpe', 'group', 'port', 'remote',
+ 'source-address', 'source-interface', 'vni',
+ 'parameters ip dont-fragment', 'parameters ip tos',
+ 'parameters ip ttl']:
+ if leaf_node_changed(conf, cli_option.split()):
+ vxlan.update({'rebuild_required': {}})
+
# We need to verify that no other VXLAN tunnel is configured when external
# mode is in use - Linux Kernel limitation
conf.set_level(base)
@@ -70,8 +82,7 @@ def verify(vxlan):
if 'group' in vxlan:
if 'source_interface' not in vxlan:
- raise ConfigError('Multicast VXLAN requires an underlaying interface ')
-
+ raise ConfigError('Multicast VXLAN requires an underlaying interface')
verify_source_interface(vxlan)
if not any(tmp in ['group', 'remote', 'source_address'] for tmp in vxlan):
@@ -108,22 +119,42 @@ def verify(vxlan):
raise ConfigError(f'Underlaying device MTU is to small ({lower_mtu} '\
f'bytes) for VXLAN overhead ({vxlan_overhead} bytes!)')
+ # Check for mixed IPv4 and IPv6 addresses
+ protocol = None
+ if 'source_address' in vxlan:
+ if is_ipv6(vxlan['source_address']):
+ protocol = 'ipv6'
+ else:
+ protocol = 'ipv4'
+
+ if 'remote' in vxlan:
+ error_msg = 'Can not mix both IPv4 and IPv6 for VXLAN underlay'
+ for remote in vxlan['remote']:
+ if is_ipv6(remote):
+ if protocol == 'ipv4':
+ raise ConfigError(error_msg)
+ protocol = 'ipv6'
+ else:
+ if protocol == 'ipv6':
+ raise ConfigError(error_msg)
+ protocol = 'ipv4'
+
verify_mtu_ipv6(vxlan)
verify_address(vxlan)
+ verify_mirror_redirect(vxlan)
return None
-
def generate(vxlan):
return None
-
def apply(vxlan):
# Check if the VXLAN interface already exists
- if vxlan['ifname'] in interfaces():
- v = VXLANIf(vxlan['ifname'])
- # VXLAN is super picky and the tunnel always needs to be recreated,
- # thus we can simply always delete it first.
- v.remove()
+ if 'rebuild_required' in vxlan or 'delete' in vxlan:
+ if vxlan['ifname'] in interfaces():
+ v = VXLANIf(vxlan['ifname'])
+ # VXLAN is super picky and the tunnel always needs to be recreated,
+ # thus we can simply always delete it first.
+ v.remove()
if 'deleted' not in vxlan:
# Finally create the new interface
@@ -132,7 +163,6 @@ def apply(vxlan):
return None
-
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index da64dd076..b404375d6 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -28,6 +28,7 @@ from vyos.configverify import verify_vrf
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mtu_ipv6
+from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import WireGuardIf
from vyos.util import check_kmod
from vyos.util import check_port_availability
@@ -70,6 +71,7 @@ def verify(wireguard):
verify_mtu_ipv6(wireguard)
verify_address(wireguard)
verify_vrf(wireguard)
+ verify_mirror_redirect(wireguard)
if 'private_key' not in wireguard:
raise ConfigError('Wireguard private-key not defined')
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index af35b5f03..500952df1 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -27,6 +27,7 @@ from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_dhcpv6
from vyos.configverify import verify_source_interface
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ifconfig import WiFiIf
@@ -189,6 +190,7 @@ def verify(wifi):
verify_address(wifi)
verify_vrf(wifi)
+ verify_mirror_redirect(wifi)
# use common function to verify VLAN configuration
verify_vlan_config(wifi)
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index a4b033374..9a33039a3 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -21,8 +21,10 @@ from time import sleep
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_authentication
from vyos.configverify import verify_interface_exists
+from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_vrf
from vyos.ifconfig import WWANIf
from vyos.util import cmd
@@ -36,7 +38,7 @@ from vyos import airbag
airbag.enable()
service_name = 'ModemManager.service'
-cron_script = '/etc/cron.d/wwan'
+cron_script = '/etc/cron.d/vyos-wwan'
def get_config(config=None):
"""
@@ -50,6 +52,32 @@ def get_config(config=None):
base = ['interfaces', 'wwan']
wwan = get_interface_dict(conf, base)
+ # We should only terminate the WWAN session if critical parameters change.
+ # All parameters that can be changed on-the-fly (like interface description)
+ # should not lead to a reconnect!
+ tmp = leaf_node_changed(conf, ['address'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = leaf_node_changed(conf, ['apn'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = leaf_node_changed(conf, ['disable'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = leaf_node_changed(conf, ['vrf'])
+ # leaf_node_changed() returns a list, as VRF is a non-multi node, there
+ # will be only one list element
+ if tmp: wwan.update({'vrf_old': tmp[0]})
+
+ tmp = leaf_node_changed(conf, ['authentication', 'user'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = leaf_node_changed(conf, ['authentication', 'password'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = leaf_node_changed(conf, ['ipv6', 'address', 'autoconf'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
# We need to know the amount of other WWAN interfaces as ModemManager needs
# to be started or stopped.
conf.set_level(base)
@@ -57,8 +85,8 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
- # This if-clause is just to be sure - it will always evaluate to true
ifname = wwan['ifname']
+ # This if-clause is just to be sure - it will always evaluate to true
if ifname in wwan['other_interfaces']:
del wwan['other_interfaces'][ifname]
if len(wwan['other_interfaces']) == 0:
@@ -77,18 +105,31 @@ def verify(wwan):
verify_interface_exists(ifname)
verify_authentication(wwan)
verify_vrf(wwan)
+ verify_mirror_redirect(wwan)
return None
def generate(wwan):
if 'deleted' in wwan:
+ # We are the last WWAN interface - there are no other ones remaining
+ # thus the cronjob needs to go away, too
+ if 'other_interfaces' not in wwan:
+ if os.path.exists(cron_script):
+ os.unlink(cron_script)
return None
+ # Install cron triggered helper script to re-dial WWAN interfaces on
+ # disconnect - e.g. happens during RF signal loss. The script watches every
+ # WWAN interface - so there is only one instance.
if not os.path.exists(cron_script):
write_file(cron_script, '*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py')
+
return None
def apply(wwan):
+ # ModemManager is required to dial WWAN connections - one instance is
+ # required to serve all modems. Activate ModemManager on first invocation
+ # of any WWAN interface.
if not is_systemd_service_active(service_name):
cmd(f'systemctl start {service_name}')
@@ -101,17 +142,19 @@ def apply(wwan):
break
sleep(0.250)
- # we only need the modem number. wwan0 -> 0, wwan1 -> 1
- modem = wwan['ifname'].lstrip('wwan')
- base_cmd = f'mmcli --modem {modem}'
- # Number of bearers is limited - always disconnect first
- cmd(f'{base_cmd} --simple-disconnect')
+ if 'shutdown_required' in wwan:
+ # we only need the modem number. wwan0 -> 0, wwan1 -> 1
+ modem = wwan['ifname'].lstrip('wwan')
+ base_cmd = f'mmcli --modem {modem}'
+ # Number of bearers is limited - always disconnect first
+ cmd(f'{base_cmd} --simple-disconnect')
w = WWANIf(wwan['ifname'])
if 'deleted' in wwan or 'disable' in wwan:
w.remove()
- # There are no other WWAN interfaces - stop the daemon
+ # We are the last WWAN interface - there are no other WWAN interfaces
+ # remaining, thus we can stop ModemManager and free resources.
if 'other_interfaces' not in wwan:
cmd(f'systemctl stop {service_name}')
# Clean CRON helper script which is used for to re-connect when
@@ -121,27 +164,25 @@ def apply(wwan):
return None
- ip_type = 'ipv4'
- slaac = dict_search('ipv6.address.autoconf', wwan) != None
- if 'address' in wwan:
- if 'dhcp' in wwan['address'] and ('dhcpv6' in wwan['address'] or slaac):
- ip_type = 'ipv4v6'
- elif 'dhcpv6' in wwan['address'] or slaac:
- ip_type = 'ipv6'
- elif 'dhcp' in wwan['address']:
- ip_type = 'ipv4'
-
- options = f'ip-type={ip_type},apn=' + wwan['apn']
- if 'authentication' in wwan:
- options += ',user={user},password={password}'.format(**wwan['authentication'])
-
- command = f'{base_cmd} --simple-connect="{options}"'
- call(command, stdout=DEVNULL)
- w.update(wwan)
+ if 'shutdown_required' in wwan:
+ ip_type = 'ipv4'
+ slaac = dict_search('ipv6.address.autoconf', wwan) != None
+ if 'address' in wwan:
+ if 'dhcp' in wwan['address'] and ('dhcpv6' in wwan['address'] or slaac):
+ ip_type = 'ipv4v6'
+ elif 'dhcpv6' in wwan['address'] or slaac:
+ ip_type = 'ipv6'
+ elif 'dhcp' in wwan['address']:
+ ip_type = 'ipv4'
- if 'other_interfaces' not in wwan and 'deleted' in wwan:
- cmd(f'systemctl start {service_name}')
+ options = f'ip-type={ip_type},apn=' + wwan['apn']
+ if 'authentication' in wwan:
+ options += ',user={user},password={password}'.format(**wwan['authentication'])
+ command = f'{base_cmd} --simple-connect="{options}"'
+ call(command, stdout=DEVNULL)
+
+ w.update(wwan)
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py
index 082c3e128..db8328259 100755
--- a/src/conf_mode/lldp.py
+++ b/src/conf_mode/lldp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017-2020 VyOS maintainers and contributors
+# Copyright (C) 2017-2022 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
@@ -15,19 +15,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
-from copy import deepcopy
from sys import exit
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.validate import is_addr_assigned
from vyos.validate import is_loopback_addr
from vyos.version import get_version_data
-from vyos import ConfigError
from vyos.util import call
+from vyos.util import dict_search
+from vyos.xml import defaults
from vyos.template import render
-
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -35,178 +35,73 @@ config_file = "/etc/default/lldpd"
vyos_config_file = "/etc/lldpd.d/01-vyos.conf"
base = ['service', 'lldp']
-default_config_data = {
- "options": '',
- "interface_list": '',
- "location": ''
-}
-
-def get_options(config):
- options = {}
- config.set_level(base)
-
- options['listen_vlan'] = config.exists('listen-vlan')
- options['mgmt_addr'] = []
- for addr in config.return_values('management-address'):
- if is_addr_assigned(addr) and not is_loopback_addr(addr):
- options['mgmt_addr'].append(addr)
- else:
- message = 'WARNING: LLDP management address {0} invalid - '.format(addr)
- if is_loopback_addr(addr):
- message += '(loopback address).'
- else:
- message += 'address not found.'
- print(message)
-
- snmp = config.exists('snmp enable')
- options["snmp"] = snmp
- if snmp:
- config.set_level('')
- options["sys_snmp"] = config.exists('service snmp')
- config.set_level(base)
-
- config.set_level(base + ['legacy-protocols'])
- options['cdp'] = config.exists('cdp')
- options['edp'] = config.exists('edp')
- options['fdp'] = config.exists('fdp')
- options['sonmp'] = config.exists('sonmp')
-
- # start with an unknown version information
- version_data = get_version_data()
- options['description'] = version_data['version']
- options['listen_on'] = []
-
- return options
-
-def get_interface_list(config):
- config.set_level(base)
- intfs_names = config.list_nodes(['interface'])
- if len(intfs_names) < 0:
- return 0
-
- interface_list = []
- for name in intfs_names:
- config.set_level(base + ['interface', name])
- disable = config.exists(['disable'])
- intf = {
- 'name': name,
- 'disable': disable
- }
- interface_list.append(intf)
- return interface_list
-
-
-def get_location_intf(config, name):
- path = base + ['interface', name]
- config.set_level(path)
-
- config.set_level(path + ['location'])
- elin = ''
- coordinate_based = {}
-
- if config.exists('elin'):
- elin = config.return_value('elin')
-
- if config.exists('coordinate-based'):
- config.set_level(path + ['location', 'coordinate-based'])
-
- coordinate_based['latitude'] = config.return_value(['latitude'])
- coordinate_based['longitude'] = config.return_value(['longitude'])
-
- coordinate_based['altitude'] = '0'
- if config.exists(['altitude']):
- coordinate_based['altitude'] = config.return_value(['altitude'])
-
- coordinate_based['datum'] = 'WGS84'
- if config.exists(['datum']):
- coordinate_based['datum'] = config.return_value(['datum'])
-
- intf = {
- 'name': name,
- 'elin': elin,
- 'coordinate_based': coordinate_based
-
- }
- return intf
-
-
-def get_location(config):
- config.set_level(base)
- intfs_names = config.list_nodes(['interface'])
- if len(intfs_names) < 0:
- return 0
-
- if config.exists('disable'):
- return 0
-
- intfs_location = []
- for name in intfs_names:
- intf = get_location_intf(config, name)
- intfs_location.append(intf)
-
- return intfs_location
-
-
def get_config(config=None):
- lldp = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
+
if not conf.exists(base):
- return None
- else:
- lldp['options'] = get_options(conf)
- lldp['interface_list'] = get_interface_list(conf)
- lldp['location'] = get_location(conf)
+ return {}
- return lldp
+ lldp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+ if conf.exists(['service', 'snmp']):
+ lldp['system_snmp_enabled'] = ''
+
+ version_data = get_version_data()
+ lldp['version'] = version_data['version']
+
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ # location coordinates have a default value
+ if 'interface' in lldp:
+ for interface, interface_config in lldp['interface'].items():
+ default_values = defaults(base + ['interface'])
+ if dict_search('location.coordinate_based', interface_config) == None:
+ # no location specified - no need to add defaults
+ del default_values['location']['coordinate_based']['datum']
+ del default_values['location']['coordinate_based']['altitude']
+
+ # cleanup default_values dictionary from inner to outer
+ # this might feel overkill here, but it does support easy extension
+ # in the future with additional default values
+ if len(default_values['location']['coordinate_based']) == 0:
+ del default_values['location']['coordinate_based']
+ if len(default_values['location']) == 0:
+ del default_values['location']
+
+ lldp['interface'][interface] = dict_merge(default_values,
+ lldp['interface'][interface])
+
+ return lldp
def verify(lldp):
# bail out early - looks like removal from running config
if lldp is None:
return
- # check location
- for location in lldp['location']:
- # check coordinate-based
- if len(location['coordinate_based']) > 0:
- # check longitude and latitude
- if not location['coordinate_based']['longitude']:
- raise ConfigError('Must define longitude for interface {0}'.format(location['name']))
-
- if not location['coordinate_based']['latitude']:
- raise ConfigError('Must define latitude for interface {0}'.format(location['name']))
-
- if not re.match(r'^(\d+)(\.\d+)?[nNsS]$', location['coordinate_based']['latitude']):
- raise ConfigError('Invalid location for interface {0}:\n' \
- 'latitude should be a number followed by S or N'.format(location['name']))
-
- if not re.match(r'^(\d+)(\.\d+)?[eEwW]$', location['coordinate_based']['longitude']):
- raise ConfigError('Invalid location for interface {0}:\n' \
- 'longitude should be a number followed by E or W'.format(location['name']))
-
- # check altitude and datum if exist
- if location['coordinate_based']['altitude']:
- if not re.match(r'^[-+0-9\.]+$', location['coordinate_based']['altitude']):
- raise ConfigError('Invalid location for interface {0}:\n' \
- 'altitude should be a positive or negative number'.format(location['name']))
-
- if location['coordinate_based']['datum']:
- if not re.match(r'^(WGS84|NAD83|MLLW)$', location['coordinate_based']['datum']):
- raise ConfigError("Invalid location for interface {0}:\n' \
- 'datum should be WGS84, NAD83, or MLLW".format(location['name']))
-
- # check elin
- elif location['elin']:
- if not re.match(r'^[0-9]{10,25}$', location['elin']):
- raise ConfigError('Invalid location for interface {0}:\n' \
- 'ELIN number must be between 10-25 numbers'.format(location['name']))
+ if 'management_address' in lldp:
+ for address in lldp['management_address']:
+ message = f'WARNING: LLDP management address "{address}" is invalid'
+ if is_loopback_addr(address):
+ print(f'{message} - loopback address')
+ elif not is_addr_assigned(address):
+ print(f'{message} - not assigned to any interface')
+
+ if 'interface' in lldp:
+ for interface, interface_config in lldp['interface'].items():
+ # bail out early if no location info present in interface config
+ if 'location' not in interface_config:
+ continue
+ if 'coordinate_based' in interface_config['location']:
+ if not {'latitude', 'latitude'} <= set(interface_config['location']['coordinate_based']):
+ raise ConfigError(f'Must define both longitude and latitude for "{interface}" location!')
# check options
- if lldp['options']['snmp']:
- if not lldp['options']['sys_snmp']:
+ if 'snmp' in lldp and 'enable' in lldp['snmp']:
+ if 'system_snmp_enabled' not in lldp:
raise ConfigError('SNMP must be configured to enable LLDP SNMP')
@@ -215,29 +110,17 @@ def generate(lldp):
if lldp is None:
return
- # generate listen on interfaces
- for intf in lldp['interface_list']:
- tmp = ''
- # add exclamation mark if interface is disabled
- if intf['disable']:
- tmp = '!'
-
- tmp += intf['name']
- lldp['options']['listen_on'].append(tmp)
-
- # generate /etc/default/lldpd
render(config_file, 'lldp/lldpd.tmpl', lldp)
- # generate /etc/lldpd.d/01-vyos.conf
render(vyos_config_file, 'lldp/vyos.conf.tmpl', lldp)
-
def apply(lldp):
+ systemd_service = 'lldpd.service'
if lldp:
# start/restart lldp service
- call('systemctl restart lldpd.service')
+ call(f'systemctl restart {systemd_service}')
else:
# LLDP service has been terminated
- call('systemctl stop lldpd.service')
+ call(f'systemctl stop {systemd_service}')
if os.path.isfile(config_file):
os.unlink(config_file)
if os.path.isfile(vyos_config_file):
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 96f8f6fb6..9f319fc8a 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -28,6 +28,7 @@ from vyos.configdict import dict_merge
from vyos.template import render
from vyos.template import is_ip_network
from vyos.util import cmd
+from vyos.util import run
from vyos.util import check_kmod
from vyos.util import dict_search
from vyos.validate import is_addr_assigned
@@ -179,12 +180,19 @@ def verify(nat):
return None
def generate(nat):
- render(nftables_nat_config, 'firewall/nftables-nat.tmpl', nat,
- permission=0o755)
+ render(nftables_nat_config, 'firewall/nftables-nat.tmpl', nat)
+
+ # dry-run newly generated configuration
+ tmp = run(f'nft -c -f {nftables_nat_config}')
+ if tmp > 0:
+ if os.path.exists(nftables_ct_file):
+ os.unlink(nftables_ct_file)
+ raise ConfigError('Configuration file errors encountered!')
+
return None
def apply(nat):
- cmd(f'{nftables_nat_config}')
+ cmd(f'nft -f {nftables_nat_config}')
if os.path.isfile(nftables_nat_config):
os.unlink(nftables_nat_config)
diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py
index 539189442..3f834f55c 100755
--- a/src/conf_mode/policy-local-route.py
+++ b/src/conf_mode/policy-local-route.py
@@ -18,6 +18,7 @@ import os
from sys import exit
+from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
@@ -35,35 +36,92 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['policy', 'local-route']
+ base = ['policy']
+
pbr = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # delete policy local-route
- dict = {}
- tmp = node_changed(conf, ['policy', 'local-route', 'rule'], key_mangling=('-', '_'))
- if tmp:
- for rule in (tmp or []):
- src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
- fwmk = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'fwmark'])
- if src:
- dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)
- pbr.update(dict)
- if fwmk:
- dict = dict_merge({'rule_remove' : {rule : {'fwmark' : fwmk}}}, dict)
+ for route in ['local_route', 'local_route6']:
+ dict_id = 'rule_remove' if route == 'local_route' else 'rule6_remove'
+ route_key = 'local-route' if route == 'local_route' else 'local-route6'
+ base_rule = base + [route_key, 'rule']
+
+ # delete policy local-route
+ dict = {}
+ tmp = node_changed(conf, base_rule, key_mangling=('-', '_'))
+ if tmp:
+ for rule in (tmp or []):
+ src = leaf_node_changed(conf, base_rule + [rule, 'source'])
+ fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark'])
+ iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface'])
+ dst = leaf_node_changed(conf, base_rule + [rule, 'destination'])
+ rule_def = {}
+ if src:
+ rule_def = dict_merge({'source' : src}, rule_def)
+ if fwmk:
+ rule_def = dict_merge({'fwmark' : fwmk}, rule_def)
+ if iif:
+ rule_def = dict_merge({'inbound_interface' : iif}, rule_def)
+ if dst:
+ rule_def = dict_merge({'destination' : dst}, rule_def)
+ dict = dict_merge({dict_id : {rule : rule_def}}, dict)
pbr.update(dict)
- # delete policy local-route rule x source x.x.x.x
- # delete policy local-route rule x fwmark x
- if 'rule' in pbr:
- for rule in pbr['rule']:
- src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
- fwmk = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'fwmark'])
- if src:
- dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)
- pbr.update(dict)
- if fwmk:
- dict = dict_merge({'rule_remove' : {rule : {'fwmark' : fwmk}}}, dict)
- pbr.update(dict)
+ if not route in pbr:
+ continue
+
+ # delete policy local-route rule x source x.x.x.x
+ # delete policy local-route rule x fwmark x
+ # delete policy local-route rule x destination x.x.x.x
+ if 'rule' in pbr[route]:
+ for rule, rule_config in pbr[route]['rule'].items():
+ src = leaf_node_changed(conf, base_rule + [rule, 'source'])
+ fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark'])
+ iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface'])
+ dst = leaf_node_changed(conf, base_rule + [rule, 'destination'])
+ # keep track of changes in configuration
+ # otherwise we might remove an existing node although nothing else has changed
+ changed = False
+
+ rule_def = {}
+ # src is None if there are no changes to src
+ if src is None:
+ # if src hasn't changed, include it in the removal selector
+ # if a new selector is added, we have to remove all previous rules without this selector
+ # to make sure we remove all previous rules with this source(s), it will be included
+ if 'source' in rule_config:
+ rule_def = dict_merge({'source': rule_config['source']}, rule_def)
+ else:
+ # if src is not None, it's previous content will be returned
+ # this can be an empty array if it's just being set, or the previous value
+ # either way, something has to be changed and we only want to remove previous values
+ changed = True
+ # set the old value for removal if it's not empty
+ if len(src) > 0:
+ rule_def = dict_merge({'source' : src}, rule_def)
+ if fwmk is None:
+ if 'fwmark' in rule_config:
+ rule_def = dict_merge({'fwmark': rule_config['fwmark']}, rule_def)
+ else:
+ changed = True
+ if len(fwmk) > 0:
+ rule_def = dict_merge({'fwmark' : fwmk}, rule_def)
+ if iif is None:
+ if 'inbound_interface' in rule_config:
+ rule_def = dict_merge({'inbound_interface': rule_config['inbound_interface']}, rule_def)
+ else:
+ changed = True
+ if len(iif) > 0:
+ rule_def = dict_merge({'inbound_interface' : iif}, rule_def)
+ if dst is None:
+ if 'destination' in rule_config:
+ rule_def = dict_merge({'destination': rule_config['destination']}, rule_def)
+ else:
+ changed = True
+ if len(dst) > 0:
+ rule_def = dict_merge({'destination' : dst}, rule_def)
+ if changed:
+ dict = dict_merge({dict_id : {rule : rule_def}}, dict)
+ pbr.update(dict)
return pbr
@@ -72,13 +130,25 @@ def verify(pbr):
if not pbr:
return None
- if 'rule' in pbr:
- for rule in pbr['rule']:
- if 'source' not in pbr['rule'][rule] and 'fwmark' not in pbr['rule'][rule]:
- raise ConfigError('Source address or fwmark is required!')
- else:
- if 'set' not in pbr['rule'][rule] or 'table' not in pbr['rule'][rule]['set']:
- raise ConfigError('Table set is required!')
+ for route in ['local_route', 'local_route6']:
+ if not route in pbr:
+ continue
+
+ pbr_route = pbr[route]
+ if 'rule' in pbr_route:
+ for rule in pbr_route['rule']:
+ if 'source' not in pbr_route['rule'][rule] \
+ and 'destination' not in pbr_route['rule'][rule] \
+ and 'fwmark' not in pbr_route['rule'][rule] \
+ and 'inbound_interface' not in pbr_route['rule'][rule]:
+ raise ConfigError('Source or destination address or fwmark or inbound-interface is required!')
+ else:
+ if 'set' not in pbr_route['rule'][rule] or 'table' not in pbr_route['rule'][rule]['set']:
+ raise ConfigError('Table set is required!')
+ if 'inbound_interface' in pbr_route['rule'][rule]:
+ interface = pbr_route['rule'][rule]['inbound_interface']
+ if interface not in interfaces():
+ raise ConfigError(f'Interface "{interface}" does not exist')
return None
@@ -93,36 +163,51 @@ def apply(pbr):
return None
# Delete old rule if needed
- if 'rule_remove' in pbr:
- for rule in pbr['rule_remove']:
- if 'source' in pbr['rule_remove'][rule]:
- for src in pbr['rule_remove'][rule]['source']:
- call(f'ip rule del prio {rule} from {src}')
- if 'fwmark' in pbr['rule_remove'][rule]:
- for fwmk in pbr['rule_remove'][rule]['fwmark']:
- call(f'ip rule del prio {rule} from all fwmark {fwmk}')
+ for rule_rm in ['rule_remove', 'rule6_remove']:
+ if rule_rm in pbr:
+ v6 = " -6" if rule_rm == 'rule6_remove' else ""
+ for rule, rule_config in pbr[rule_rm].items():
+ rule_config['source'] = rule_config['source'] if 'source' in rule_config else ['']
+ for src in rule_config['source']:
+ f_src = '' if src == '' else f' from {src} '
+ rule_config['destination'] = rule_config['destination'] if 'destination' in rule_config else ['']
+ for dst in rule_config['destination']:
+ f_dst = '' if dst == '' else f' to {dst} '
+ rule_config['fwmark'] = rule_config['fwmark'] if 'fwmark' in rule_config else ['']
+ for fwmk in rule_config['fwmark']:
+ f_fwmk = '' if fwmk == '' else f' fwmark {fwmk} '
+ rule_config['inbound_interface'] = rule_config['inbound_interface'] if 'inbound_interface' in rule_config else ['']
+ for iif in rule_config['inbound_interface']:
+ f_iif = '' if iif == '' else f' iif {iif} '
+ call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif}')
# Generate new config
- if 'rule' in pbr:
- for rule in pbr['rule']:
- table = pbr['rule'][rule]['set']['table']
- # Only source in the rule
- # set policy local-route rule 100 source '203.0.113.1'
- if 'source' in pbr['rule'][rule] and not 'fwmark' in pbr['rule'][rule]:
- for src in pbr['rule'][rule]['source']:
- call(f'ip rule add prio {rule} from {src} lookup {table}')
- # Only fwmark in the rule
- # set policy local-route rule 101 fwmark '23'
- if 'fwmark' in pbr['rule'][rule] and not 'source' in pbr['rule'][rule]:
- fwmk = pbr['rule'][rule]['fwmark']
- call(f'ip rule add prio {rule} from all fwmark {fwmk} lookup {table}')
- # Source and fwmark in the rule
- # set policy local-route rule 100 source '203.0.113.1'
- # set policy local-route rule 100 fwmark '23'
- if 'source' in pbr['rule'][rule] and 'fwmark' in pbr['rule'][rule]:
- fwmk = pbr['rule'][rule]['fwmark']
- for src in pbr['rule'][rule]['source']:
- call(f'ip rule add prio {rule} from {src} fwmark {fwmk} lookup {table}')
+ for route in ['local_route', 'local_route6']:
+ if not route in pbr:
+ continue
+
+ v6 = " -6" if route == 'local_route6' else ""
+
+ pbr_route = pbr[route]
+ if 'rule' in pbr_route:
+ for rule, rule_config in pbr_route['rule'].items():
+ table = rule_config['set']['table']
+
+ rule_config['source'] = rule_config['source'] if 'source' in rule_config else ['all']
+ for src in rule_config['source'] or ['all']:
+ f_src = '' if src == '' else f' from {src} '
+ rule_config['destination'] = rule_config['destination'] if 'destination' in rule_config else ['all']
+ for dst in rule_config['destination']:
+ f_dst = '' if dst == '' else f' to {dst} '
+ f_fwmk = ''
+ if 'fwmark' in rule_config:
+ fwmk = rule_config['fwmark']
+ f_fwmk = f' fwmark {fwmk} '
+ f_iif = ''
+ if 'inbound_interface' in rule_config:
+ iif = rule_config['inbound_interface']
+ f_iif = f' iif {iif} '
+ call(f'ip{v6} rule add prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif} lookup {table}')
return None
diff --git a/src/conf_mode/policy-route-interface.py b/src/conf_mode/policy-route-interface.py
index e81135a74..1108aebe6 100755
--- a/src/conf_mode/policy-route-interface.py
+++ b/src/conf_mode/policy-route-interface.py
@@ -52,7 +52,7 @@ def verify(if_policy):
if not if_policy:
return None
- for route in ['route', 'ipv6_route']:
+ for route in ['route', 'route6']:
if route in if_policy:
if route not in if_policy['policy']:
raise ConfigError('Policy route not configured')
@@ -71,7 +71,7 @@ def cleanup_rule(table, chain, ifname, new_name=None):
results = cmd(f'nft -a list chain {table} {chain}').split("\n")
retval = None
for line in results:
- if f'oifname "{ifname}"' in line:
+ if f'ifname "{ifname}"' in line:
if new_name and f'jump {new_name}' in line:
# new_name is used to clear rules for any previously referenced chains
# returns true when rule exists and doesn't need to be created
@@ -98,8 +98,8 @@ def apply(if_policy):
else:
cleanup_rule('ip mangle', route_chain, ifname)
- if 'ipv6_route' in if_policy:
- name = 'VYOS_PBR6_' + if_policy['ipv6_route']
+ if 'route6' in if_policy:
+ name = 'VYOS_PBR6_' + if_policy['route6']
rule_exists = cleanup_rule('ip6 mangle', ipv6_route_chain, ifname, name)
if not rule_exists:
diff --git a/src/conf_mode/policy-route.py b/src/conf_mode/policy-route.py
index d098be68d..3d1d7d8c5 100755
--- a/src/conf_mode/policy-route.py
+++ b/src/conf_mode/policy-route.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import re
from json import loads
from sys import exit
@@ -31,6 +32,35 @@ airbag.enable()
mark_offset = 0x7FFFFFFF
nftables_conf = '/run/nftables_policy.conf'
+preserve_chains = [
+ 'VYOS_PBR_PREROUTING',
+ 'VYOS_PBR_POSTROUTING',
+ 'VYOS_PBR6_PREROUTING',
+ 'VYOS_PBR6_POSTROUTING'
+]
+
+valid_groups = [
+ 'address_group',
+ 'network_group',
+ 'port_group'
+]
+
+def get_policy_interfaces(conf):
+ out = {}
+ interfaces = conf.get_config_dict(['interfaces'], key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
+ def find_interfaces(iftype_conf, output={}, prefix=''):
+ for ifname, if_conf in iftype_conf.items():
+ if 'policy' in if_conf:
+ output[prefix + ifname] = if_conf['policy']
+ for vif in ['vif', 'vif_s', 'vif_c']:
+ if vif in if_conf:
+ output.update(find_interfaces(if_conf[vif], output, f'{prefix}{ifname}.'))
+ return output
+ for iftype, iftype_conf in interfaces.items():
+ out.update(find_interfaces(iftype_conf))
+ return out
+
def get_config(config=None):
if config:
conf = config
@@ -38,68 +68,149 @@ def get_config(config=None):
conf = Config()
base = ['policy']
- if not conf.exists(base + ['route']) and not conf.exists(base + ['ipv6-route']):
- return None
-
policy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
+ policy['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
+ policy['interfaces'] = get_policy_interfaces(conf)
+
return policy
-def verify(policy):
- # bail out early - looks like removal from running config
- if not policy:
- return None
+def verify_rule(policy, name, rule_conf, ipv6):
+ icmp = 'icmp' if not ipv6 else 'icmpv6'
+ if icmp in rule_conf:
+ icmp_defined = False
+ if 'type_name' in rule_conf[icmp]:
+ icmp_defined = True
+ if 'code' in rule_conf[icmp] or 'type' in rule_conf[icmp]:
+ raise ConfigError(f'{name} rule {rule_id}: Cannot use ICMP type/code with ICMP type-name')
+ if 'code' in rule_conf[icmp]:
+ icmp_defined = True
+ if 'type' not in rule_conf[icmp]:
+ raise ConfigError(f'{name} rule {rule_id}: ICMP code can only be defined if ICMP type is defined')
+ if 'type' in rule_conf[icmp]:
+ icmp_defined = True
+
+ if icmp_defined and 'protocol' not in rule_conf or rule_conf['protocol'] != icmp:
+ raise ConfigError(f'{name} rule {rule_id}: ICMP type/code or type-name can only be defined if protocol is ICMP')
+
+ if 'set' in rule_conf:
+ if 'tcp_mss' in rule_conf['set']:
+ tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
+ if not tcp_flags or 'syn' not in tcp_flags:
+ raise ConfigError(f'{name} rule {rule_id}: TCP SYN flag must be set to modify TCP-MSS')
+
+ tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
+ if tcp_flags:
+ if dict_search_args(rule_conf, 'protocol') != 'tcp':
+ raise ConfigError('Protocol must be tcp when specifying tcp flags')
+
+ not_flags = dict_search_args(rule_conf, 'tcp', 'flags', 'not')
+ if not_flags:
+ duplicates = [flag for flag in tcp_flags if flag in not_flags]
+ if duplicates:
+ raise ConfigError(f'Cannot match a tcp flag as set and not set')
+
+ for side in ['destination', 'source']:
+ if side in rule_conf:
+ side_conf = rule_conf[side]
+
+ if 'group' in side_conf:
+ if {'address_group', 'network_group'} <= set(side_conf['group']):
+ raise ConfigError('Only one address-group or network-group can be specified')
+
+ for group in valid_groups:
+ if group in side_conf['group']:
+ group_name = side_conf['group'][group]
+
+ if group_name.startswith('!'):
+ group_name = group_name[1:]
+
+ fw_group = f'ipv6_{group}' if ipv6 and group in ['address_group', 'network_group'] else group
+ error_group = fw_group.replace("_", "-")
+ group_obj = dict_search_args(policy['firewall_group'], fw_group, group_name)
- for route in ['route', 'ipv6_route']:
+ if group_obj is None:
+ raise ConfigError(f'Invalid {error_group} "{group_name}" on policy route rule')
+
+ if not group_obj:
+ print(f'WARNING: {error_group} "{group_name}" has no members')
+
+ if 'port' in side_conf or dict_search_args(side_conf, 'group', 'port_group'):
+ if 'protocol' not in rule_conf:
+ raise ConfigError('Protocol must be defined if specifying a port or port-group')
+
+ if rule_conf['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
+ raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port or port-group')
+
+def verify(policy):
+ for route in ['route', 'route6']:
+ ipv6 = route == 'route6'
if route in policy:
for name, pol_conf in policy[route].items():
if 'rule' in pol_conf:
- for rule_id, rule_conf in pol_conf.items():
- icmp = 'icmp' if route == 'route' else 'icmpv6'
- if icmp in rule_conf:
- icmp_defined = False
- if 'type_name' in rule_conf[icmp]:
- icmp_defined = True
- if 'code' in rule_conf[icmp] or 'type' in rule_conf[icmp]:
- raise ConfigError(f'{name} rule {rule_id}: Cannot use ICMP type/code with ICMP type-name')
- if 'code' in rule_conf[icmp]:
- icmp_defined = True
- if 'type' not in rule_conf[icmp]:
- raise ConfigError(f'{name} rule {rule_id}: ICMP code can only be defined if ICMP type is defined')
- if 'type' in rule_conf[icmp]:
- icmp_defined = True
-
- if icmp_defined and 'protocol' not in rule_conf or rule_conf['protocol'] != icmp:
- raise ConfigError(f'{name} rule {rule_id}: ICMP type/code or type-name can only be defined if protocol is ICMP')
- if 'set' in rule_conf:
- if 'tcp_mss' in rule_conf['set']:
- tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
- if not tcp_flags or 'SYN' not in tcp_flags.split(","):
- raise ConfigError(f'{name} rule {rule_id}: TCP SYN flag must be set to modify TCP-MSS')
- if 'tcp' in rule_conf:
- if 'flags' in rule_conf['tcp']:
- if 'protocol' not in rule_conf or rule_conf['protocol'] != 'tcp':
- raise ConfigError(f'{name} rule {rule_id}: TCP flags can only be set if protocol is set to TCP')
+ for rule_id, rule_conf in pol_conf['rule'].items():
+ verify_rule(policy, name, rule_conf, ipv6)
+
+ for ifname, if_policy in policy['interfaces'].items():
+ name = dict_search_args(if_policy, 'route')
+ ipv6_name = dict_search_args(if_policy, 'route6')
+ if name and not dict_search_args(policy, 'route', name):
+ raise ConfigError(f'Policy route "{name}" is still referenced on interface {ifname}')
+
+ if ipv6_name and not dict_search_args(policy, 'route6', ipv6_name):
+ raise ConfigError(f'Policy route6 "{ipv6_name}" is still referenced on interface {ifname}')
return None
-def generate(policy):
- if not policy:
- if os.path.exists(nftables_conf):
- os.unlink(nftables_conf)
- return None
+def cleanup_rule(table, jump_chain):
+ commands = []
+ results = cmd(f'nft -a list table {table}').split("\n")
+ for line in results:
+ if f'jump {jump_chain}' in line:
+ handle_search = re.search('handle (\d+)', line)
+ if handle_search:
+ commands.append(f'delete rule {table} {chain} handle {handle_search[1]}')
+ return commands
+def cleanup_commands(policy):
+ commands = []
+ for table in ['ip mangle', 'ip6 mangle']:
+ json_str = cmd(f'nft -j list table {table}')
+ obj = loads(json_str)
+ if 'nftables' not in obj:
+ continue
+ for item in obj['nftables']:
+ if 'chain' in item:
+ chain = item['chain']['name']
+ if not chain.startswith("VYOS_PBR"):
+ continue
+ if chain not in preserve_chains:
+ if table == 'ip mangle' and dict_search_args(policy, 'route', chain.replace("VYOS_PBR_", "", 1)):
+ commands.append(f'flush chain {table} {chain}')
+ elif table == 'ip6 mangle' and dict_search_args(policy, 'route6', chain.replace("VYOS_PBR6_", "", 1)):
+ commands.append(f'flush chain {table} {chain}')
+ else:
+ commands += cleanup_rule(table, chain)
+ commands.append(f'delete chain {table} {chain}')
+ return commands
+
+def generate(policy):
if not os.path.exists(nftables_conf):
policy['first_install'] = True
+ else:
+ policy['cleanup_commands'] = cleanup_commands(policy)
render(nftables_conf, 'firewall/nftables-policy.tmpl', policy)
return None
def apply_table_marks(policy):
- for route in ['route', 'ipv6_route']:
+ for route in ['route', 'route6']:
if route in policy:
+ cmd_str = 'ip' if route == 'route' else 'ip -6'
+ tables = []
for name, pol_conf in policy[route].items():
if 'rule' in pol_conf:
for rule_id, rule_conf in pol_conf['rule'].items():
@@ -107,31 +218,27 @@ def apply_table_marks(policy):
if set_table:
if set_table == 'main':
set_table = '254'
+ if set_table in tables:
+ continue
+ tables.append(set_table)
table_mark = mark_offset - int(set_table)
- cmd(f'ip rule add fwmark {table_mark} table {set_table}')
+ cmd(f'{cmd_str} rule add pref {set_table} fwmark {table_mark} table {set_table}')
def cleanup_table_marks():
- json_rules = cmd('ip -j -N rule list')
- rules = loads(json_rules)
- for rule in rules:
- if 'fwmark' not in rule or 'table' not in rule:
- continue
- fwmark = rule['fwmark']
- table = int(rule['table'])
- if fwmark[:2] == '0x':
- fwmark = int(fwmark, 16)
- if (int(fwmark) == (mark_offset - table)):
- cmd(f'ip rule del fwmark {fwmark} table {table}')
+ for cmd_str in ['ip', 'ip -6']:
+ json_rules = cmd(f'{cmd_str} -j -N rule list')
+ rules = loads(json_rules)
+ for rule in rules:
+ if 'fwmark' not in rule or 'table' not in rule:
+ continue
+ fwmark = rule['fwmark']
+ table = int(rule['table'])
+ if fwmark[:2] == '0x':
+ fwmark = int(fwmark, 16)
+ if (int(fwmark) == (mark_offset - table)):
+ cmd(f'{cmd_str} rule del fwmark {fwmark} table {table}')
def apply(policy):
- if not policy or 'first_install' not in policy:
- run(f'nft flush table ip mangle')
- run(f'nft flush table ip6 mangle')
-
- if not policy:
- cleanup_table_marks()
- return None
-
install_result = run(f'nft -f {nftables_conf}')
if install_result == 1:
raise ConfigError('Failed to apply policy based routing')
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index e251396c7..9d8fcfa36 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -87,6 +87,7 @@ def verify(policy):
# human readable instance name (hypen instead of underscore)
policy_hr = policy_type.replace('_', '-')
+ entries = []
for rule, rule_config in instance_config['rule'].items():
mandatory_error = f'must be specified for "{policy_hr} {instance} rule {rule}"!'
if 'action' not in rule_config:
@@ -113,6 +114,10 @@ def verify(policy):
if 'prefix' not in rule_config:
raise ConfigError(f'A prefix {mandatory_error}')
+ if rule_config in entries:
+ raise ConfigError(f'Rule "{rule}" contains a duplicate prefix definition!')
+ entries.append(rule_config)
+
# route-maps tend to be a bit more complex so they get their own verify() section
if 'route_map' in policy:
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index d8704727c..dace53d37 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -159,13 +159,21 @@ def verify(bgp):
# Only checks for ipv4 and ipv6 neighbors
# Check if neighbor address is assigned as system interface address
- if is_ip(peer) and is_addr_assigned(peer):
- raise ConfigError(f'Can not configure a local address as neighbor "{peer}"')
+ vrf = None
+ vrf_error_msg = f' in default VRF!'
+ if 'vrf' in bgp:
+ vrf = bgp['vrf']
+ vrf_error_msg = f' in VRF "{vrf}"!'
+
+ if is_ip(peer) and is_addr_assigned(peer, vrf):
+ raise ConfigError(f'Can not configure local address as neighbor "{peer}"{vrf_error_msg}')
elif is_interface(peer):
if 'peer_group' in peer_config:
raise ConfigError(f'peer-group must be set under the interface node of "{peer}"')
if 'remote_as' in peer_config:
raise ConfigError(f'remote-as must be set under the interface node of "{peer}"')
+ if 'source_interface' in peer_config['interface']:
+ raise ConfigError(f'"source-interface" option not allowed for neighbor "{peer}"')
for afi in ['ipv4_unicast', 'ipv4_multicast', 'ipv4_labeled_unicast', 'ipv4_flowspec',
'ipv6_unicast', 'ipv6_multicast', 'ipv6_labeled_unicast', 'ipv6_flowspec',
@@ -205,6 +213,11 @@ def verify(bgp):
if 'non_exist_map' in afi_config['conditionally_advertise']:
verify_route_map(afi_config['conditionally_advertise']['non_exist_map'], bgp)
+ # T4332: bgp deterministic-med cannot be disabled while addpath-tx-bestpath-per-AS is in use
+ if 'addpath_tx_per_as' in afi_config:
+ if dict_search('parameters.deterministic_med', bgp) == None:
+ raise ConfigError('addpath-tx-per-as requires BGP deterministic-med paramtere to be set!')
+
# Validate if configured Prefix list exists
if 'prefix_list' in afi_config:
for tmp in ['import', 'export']:
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 9b4b215de..f2501e38a 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-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -169,28 +169,40 @@ def verify(isis):
# 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)
+ g_high_label_value = dict_search('segment_routing.global_block.high_label_value', isis)
+ g_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 high or low value is blank, throw error
+ if not (g_low_label_value or g_high_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 int(g_low_label_value) > int(g_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 dict_search('segment_routing.global_block', isis) == None:
+ raise ConfigError('Segment routing local-block requires global-block to be configured!')
- # 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!')
+ l_high_label_value = dict_search('segment_routing.local_block.high_label_value', isis)
+ l_low_label_value = dict_search('segment_routing.local_block.low_label_value', isis)
- # 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')
+ # If segment routing local-block high or low value is blank, throw error
+ if not (l_low_label_value or l_high_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(l_low_label_value) > int(l_high_label_value):
+ raise ConfigError('Segment routing local-block low value must be lower than high value')
+
+ # local-block most live outside global block
+ global_range = range(int(g_low_label_value), int(g_high_label_value) +1)
+ local_range = range(int(l_low_label_value), int(l_high_label_value) +1)
+
+ # Check for overlapping ranges
+ if list(set(global_range) & set(local_range)):
+ raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\
+ f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!')
return None
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 0b0c7d07b..933e23065 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -20,11 +20,10 @@ from sys import exit
from glob import glob
from vyos.config import Config
-from vyos.configdict import node_changed
from vyos.template import render_to_string
-from vyos.util import call
from vyos.util import dict_search
from vyos.util import read_file
+from vyos.util import sysctl_write
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -89,21 +88,21 @@ def apply(mpls):
labels = '0'
if 'interface' in mpls:
labels = '1048575'
- call(f'sysctl -wq net.mpls.platform_labels={labels}')
+ sysctl_write('net.mpls.platform_labels', labels)
# Check for changes in global MPLS options
if 'parameters' in mpls:
# Choose whether to copy IP TTL to MPLS header TTL
if 'no_propagate_ttl' in mpls['parameters']:
- call('sysctl -wq net.mpls.ip_ttl_propagate=0')
+ sysctl_write('net.mpls.ip_ttl_propagate', 0)
# Choose whether to limit maximum MPLS header TTL
if 'maximum_ttl' in mpls['parameters']:
ttl = mpls['parameters']['maximum_ttl']
- call(f'sysctl -wq net.mpls.default_ttl={ttl}')
+ sysctl_write('net.mpls.default_ttl', ttl)
else:
# Set default global MPLS options if not defined.
- call('sysctl -wq net.mpls.ip_ttl_propagate=1')
- call('sysctl -wq net.mpls.default_ttl=255')
+ sysctl_write('net.mpls.ip_ttl_propagate', 1)
+ sysctl_write('net.mpls.default_ttl', 255)
# Enable and disable MPLS processing on interfaces per configuration
if 'interface' in mpls:
@@ -117,11 +116,11 @@ def apply(mpls):
if '1' in interface_state:
if system_interface not in mpls['interface']:
system_interface = system_interface.replace('.', '/')
- call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0')
+ sysctl_write(f'net.mpls.conf.{system_interface}.input', 0)
elif '0' in interface_state:
if system_interface in mpls['interface']:
system_interface = system_interface.replace('.', '/')
- call(f'sysctl -wq net.mpls.conf.{system_interface}.input=1')
+ sysctl_write(f'net.mpls.conf.{system_interface}.input', 1)
else:
system_interfaces = []
# If MPLS interfaces are not configured, set MPLS processing disabled
@@ -129,7 +128,7 @@ def apply(mpls):
system_interfaces.append(os.path.basename(interface))
for system_interface in system_interfaces:
system_interface = system_interface.replace('.', '/')
- call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0')
+ sysctl_write(f'net.mpls.conf.{system_interface}.input', 0)
return None
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 4895cde6f..26d491838 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -25,6 +25,7 @@ from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_route_map
from vyos.configverify import verify_interface_exists
+from vyos.configverify import verify_access_list
from vyos.template import render_to_string
from vyos.util import dict_search
from vyos.util import get_interface_config
@@ -159,6 +160,16 @@ def verify(ospf):
route_map_name = dict_search('default_information.originate.route_map', ospf)
if route_map_name: verify_route_map(route_map_name, ospf)
+ # Validate if configured Access-list exists
+ if 'area' in ospf:
+ for area, area_config in ospf['area'].items():
+ if 'import_list' in area_config:
+ acl_import = area_config['import_list']
+ if acl_import: verify_access_list(acl_import, ospf)
+ if 'export_list' in area_config:
+ acl_export = area_config['export_list']
+ if acl_export: verify_access_list(acl_export, ospf)
+
if 'interface' in ospf:
for interface, interface_config in ospf['interface'].items():
verify_interface_exists(interface)
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index c1e427b16..f0ec48de4 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -82,6 +82,10 @@ def verify(static):
for interface, interface_config in prefix_options[type].items():
verify_vrf(interface_config)
+ if {'blackhole', 'reject'} <= set(prefix_options):
+ raise ConfigError(f'Can not use both blackhole and reject for '\
+ 'prefix "{prefix}"!')
+
return None
def generate(static):
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
new file mode 100755
index 000000000..dbe3be225
--- /dev/null
+++ b/src/conf_mode/qos.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 exit
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.xml import defaults
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['qos']
+ if not conf.exists(base):
+ return None
+
+ qos = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ if 'policy' in qos:
+ for policy in qos['policy']:
+ # CLI mangles - to _ for better Jinja2 compatibility - do we need
+ # Jinja2 here?
+ policy = policy.replace('-','_')
+
+ default_values = defaults(base + ['policy', policy])
+
+ # class is another tag node which requires individual handling
+ class_default_values = defaults(base + ['policy', policy, 'class'])
+ if 'class' in default_values:
+ del default_values['class']
+
+ for p_name, p_config in qos['policy'][policy].items():
+ qos['policy'][policy][p_name] = dict_merge(
+ default_values, qos['policy'][policy][p_name])
+
+ if 'class' in p_config:
+ for p_class in p_config['class']:
+ qos['policy'][policy][p_name]['class'][p_class] = dict_merge(
+ class_default_values, qos['policy'][policy][p_name]['class'][p_class])
+
+ import pprint
+ pprint.pprint(qos)
+ return qos
+
+def verify(qos):
+ if not qos:
+ return None
+
+ # network policy emulator
+ # reorder rerquires delay to be set
+
+ raise ConfigError('123')
+ return None
+
+def generate(qos):
+ return None
+
+def apply(qos):
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index f676fdbbe..2ebee8018 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -41,6 +41,7 @@ default_config_data = {
'interfaces': [],
'dnsv4': [],
'dnsv6': [],
+ 'client_named_ip_pool': [],
'client_ipv6_pool': [],
'client_ipv6_delegate_prefix': [],
'radius_server': [],
@@ -219,6 +220,22 @@ def get_config(config=None):
conf.set_level(base_path)
+ # Named client-ip-pool
+ if conf.exists(['client-ip-pool', 'name']):
+ for name in conf.list_nodes(['client-ip-pool', 'name']):
+ tmp = {
+ 'name': name,
+ 'gateway_address': '',
+ 'subnet': ''
+ }
+
+ if conf.exists(['client-ip-pool', 'name', name, 'gateway-address']):
+ tmp['gateway_address'] += conf.return_value(['client-ip-pool', 'name', name, 'gateway-address'])
+ if conf.exists(['client-ip-pool', 'name', name, 'subnet']):
+ tmp['subnet'] += conf.return_value(['client-ip-pool', 'name', name, 'subnet'])
+
+ ipoe['client_named_ip_pool'].append(tmp)
+
if conf.exists(['client-ipv6-pool', 'prefix']):
for prefix in conf.list_nodes(['client-ipv6-pool', 'prefix']):
tmp = {
@@ -254,10 +271,6 @@ def verify(ipoe):
if not ipoe['interfaces']:
raise ConfigError('No IPoE interface configured')
- for interface in ipoe['interfaces']:
- if not interface['range']:
- raise ConfigError(f'No IPoE client subnet defined on interface "{ interface }"')
-
if len(ipoe['dnsv4']) > 2:
raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py
index a1e7a7286..8a972b9fe 100755
--- a/src/conf_mode/service_monitoring_telegraf.py
+++ b/src/conf_mode/service_monitoring_telegraf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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,6 +22,7 @@ from shutil import rmtree
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.ifconfig import Section
from vyos.template import render
from vyos.util import call
from vyos.util import chown
@@ -42,6 +43,24 @@ systemd_telegraf_override_dir = '/etc/systemd/system/vyos-telegraf.service.d'
systemd_override = f'{systemd_telegraf_override_dir}/10-override.conf'
+def get_interfaces(type='', vlan=True):
+ """
+ Get interfaces
+ get_interfaces()
+ ['dum0', 'eth0', 'eth1', 'eth1.5', 'lo', 'tun0']
+
+ get_interfaces("dummy")
+ ['dum0']
+ """
+ interfaces = []
+ ifaces = Section.interfaces(type)
+ for iface in ifaces:
+ if vlan == False and '.' in iface:
+ continue
+ interfaces.append(iface)
+
+ return interfaces
+
def get_nft_filter_chains():
"""
Get nft chains for table filter
@@ -57,6 +76,7 @@ def get_nft_filter_chains():
return chain_list
+
def get_config(config=None):
if config:
@@ -75,8 +95,9 @@ def get_config(config=None):
default_values = defaults(base)
monitoring = dict_merge(default_values, monitoring)
- monitoring['nft_chains'] = get_nft_filter_chains()
monitoring['custom_scripts_dir'] = custom_scripts_dir
+ monitoring['interfaces_ethernet'] = get_interfaces('ethernet', vlan=False)
+ monitoring['nft_chains'] = get_nft_filter_chains()
return monitoring
diff --git a/src/conf_mode/service_upnp.py b/src/conf_mode/service_upnp.py
new file mode 100755
index 000000000..d21b31990
--- /dev/null
+++ b/src/conf_mode/service_upnp.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 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
+import uuid
+import netifaces
+from ipaddress import IPv4Network
+from ipaddress import IPv6Network
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.configdict import get_interface_dict
+from vyos.configverify import verify_vrf
+from vyos.util import call
+from vyos.template import render
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
+from vyos.xml import defaults
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+config_file = r'/run/upnp/miniupnp.conf'
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['service', 'upnp']
+ upnpd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ if not upnpd:
+ return None
+
+ if 'rule' in upnpd:
+ default_member_values = defaults(base + ['rule'])
+ for rule,rule_config in upnpd['rule'].items():
+ upnpd['rule'][rule] = dict_merge(default_member_values, upnpd['rule'][rule])
+
+ uuidgen = uuid.uuid1()
+ upnpd.update({'uuid': uuidgen})
+
+ return upnpd
+
+def get_all_interface_addr(prefix, filter_dev, filter_family):
+ list_addr = []
+ interfaces = netifaces.interfaces()
+
+ for interface in interfaces:
+ if filter_dev and interface in filter_dev:
+ continue
+ addrs = netifaces.ifaddresses(interface)
+ if netifaces.AF_INET in addrs.keys():
+ if netifaces.AF_INET in filter_family:
+ for addr in addrs[netifaces.AF_INET]:
+ if prefix:
+ # we need to manually assemble a list of IPv4 address/prefix
+ prefix = '/' + \
+ str(IPv4Network('0.0.0.0/' + addr['netmask']).prefixlen)
+ list_addr.append(addr['addr'] + prefix)
+ else:
+ list_addr.append(addr['addr'])
+ if netifaces.AF_INET6 in addrs.keys():
+ if netifaces.AF_INET6 in filter_family:
+ for addr in addrs[netifaces.AF_INET6]:
+ if prefix:
+ # we need to manually assemble a list of IPv4 address/prefix
+ bits = bin(int(addr['netmask'].replace(':', '').split('/')[0], 16)).count('1')
+ prefix = '/' + str(bits)
+ list_addr.append(addr['addr'] + prefix)
+ else:
+ list_addr.append(addr['addr'])
+
+ return list_addr
+
+def verify(upnpd):
+ if not upnpd:
+ return None
+
+ if 'wan_interface' not in upnpd:
+ raise ConfigError('To enable UPNP, you must have the "wan-interface" option!')
+
+ if 'rule' in upnpd:
+ for rule, rule_config in upnpd['rule'].items():
+ for option in ['external_port_range', 'internal_port_range', 'ip', 'action']:
+ if option not in rule_config:
+ tmp = option.replace('_', '-')
+ raise ConfigError(f'Every UPNP rule requires "{tmp}" to be set!')
+
+ if 'stun' in upnpd:
+ for option in ['host', 'port']:
+ if option not in upnpd['stun']:
+ raise ConfigError(f'A UPNP stun support must have an "{option}" option!')
+
+ # Check the validity of the IP address
+ listen_dev = []
+ system_addrs_cidr = get_all_interface_addr(True, [], [netifaces.AF_INET, netifaces.AF_INET6])
+ system_addrs = get_all_interface_addr(False, [], [netifaces.AF_INET, netifaces.AF_INET6])
+ for listen_if_or_addr in upnpd['listen']:
+ if listen_if_or_addr not in netifaces.interfaces():
+ listen_dev.append(listen_if_or_addr)
+ if (listen_if_or_addr not in system_addrs) and (listen_if_or_addr not in system_addrs_cidr) and (listen_if_or_addr not in netifaces.interfaces()):
+ if is_ipv4(listen_if_or_addr) and IPv4Network(listen_if_or_addr).is_multicast:
+ raise ConfigError(f'The address "{listen_if_or_addr}" is an address that is not allowed to listen on. It is not an interface address nor a multicast address!')
+ if is_ipv6(listen_if_or_addr) and IPv6Network(listen_if_or_addr).is_multicast:
+ raise ConfigError(f'The address "{listen_if_or_addr}" is an address that is not allowed to listen on. It is not an interface address nor a multicast address!')
+
+ system_listening_dev_addrs_cidr = get_all_interface_addr(True, listen_dev, [netifaces.AF_INET6])
+ system_listening_dev_addrs = get_all_interface_addr(False, listen_dev, [netifaces.AF_INET6])
+ for listen_if_or_addr in upnpd['listen']:
+ if listen_if_or_addr not in netifaces.interfaces() and (listen_if_or_addr not in system_listening_dev_addrs_cidr) and (listen_if_or_addr not in system_listening_dev_addrs) and is_ipv6(listen_if_or_addr) and (not IPv6Network(listen_if_or_addr).is_multicast):
+ raise ConfigError(f'{listen_if_or_addr} must listen on the interface of the network card')
+
+def generate(upnpd):
+ if not upnpd:
+ return None
+
+ if os.path.isfile(config_file):
+ os.unlink(config_file)
+
+ render(config_file, 'firewall/upnpd.conf.tmpl', upnpd)
+
+def apply(upnpd):
+ systemd_service_name = 'miniupnpd.service'
+ if not upnpd:
+ # Stop the UPNP service
+ call(f'systemctl stop {systemd_service_name}')
+ else:
+ # Start the UPNP service
+ call(f'systemctl restart {systemd_service_name}')
+
+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/system-ip.py b/src/conf_mode/system-ip.py
index 32cb2f036..05fc3a97a 100755
--- a/src/conf_mode/system-ip.py
+++ b/src/conf_mode/system-ip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -20,14 +20,13 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.util import call
from vyos.util import dict_search
+from vyos.util import sysctl_write
+from vyos.util import write_file
from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
-def sysctl(name, value):
- call(f'sysctl -wq {name}={value}')
-
def get_config(config=None):
if config:
conf = config
@@ -50,29 +49,29 @@ def generate(opt):
pass
def apply(opt):
+ # Apply ARP threshold values
+ # table_size has a default value - thus the key always exists
size = int(dict_search('arp.table_size', opt))
- if size:
- # apply ARP threshold values
- sysctl('net.ipv4.neigh.default.gc_thresh3', str(size))
- sysctl('net.ipv4.neigh.default.gc_thresh2', str(size // 2))
- sysctl('net.ipv4.neigh.default.gc_thresh1', str(size // 8))
+ # Amount upon reaching which the records begin to be cleared immediately
+ sysctl_write('net.ipv4.neigh.default.gc_thresh3', size)
+ # Amount after which the records begin to be cleaned after 5 seconds
+ sysctl_write('net.ipv4.neigh.default.gc_thresh2', size // 2)
+ # Minimum number of stored records is indicated which is not cleared
+ sysctl_write('net.ipv4.neigh.default.gc_thresh1', size // 8)
# enable/disable IPv4 forwarding
- tmp = '1'
- if 'disable_forwarding' in opt:
- tmp = '0'
- sysctl('net.ipv4.conf.all.forwarding', tmp)
+ tmp = dict_search('disable_forwarding', opt)
+ value = '0' if (tmp != None) else '1'
+ write_file('/proc/sys/net/ipv4/conf/all/forwarding', value)
- tmp = '0'
- # configure multipath - dict_search() returns an empty dict if key was found
- if isinstance(dict_search('multipath.ignore_unreachable_nexthops', opt), dict):
- tmp = '1'
- sysctl('net.ipv4.fib_multipath_use_neigh', tmp)
+ # configure multipath
+ tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
+ value = '1' if (tmp != None) else '0'
+ sysctl_write('net.ipv4.fib_multipath_use_neigh', value)
- tmp = '0'
- if isinstance(dict_search('multipath.layer4_hashing', opt), dict):
- tmp = '1'
- sysctl('net.ipv4.fib_multipath_hash_policy', tmp)
+ tmp = dict_search('multipath.layer4_hashing', opt)
+ value = '1' if (tmp != None) else '0'
+ sysctl_write('net.ipv4.fib_multipath_hash_policy', value)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py
index f70ec2631..26aacf46b 100755
--- a/src/conf_mode/system-ipv6.py
+++ b/src/conf_mode/system-ipv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -15,95 +15,68 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import sys
from sys import exit
-from copy import deepcopy
from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.util import dict_search
+from vyos.util import sysctl_write
+from vyos.util import write_file
+from vyos.xml import defaults
from vyos import ConfigError
-from vyos.util import call
-
from vyos import airbag
airbag.enable()
-ipv6_disable_file = '/etc/modprobe.d/vyos_disable_ipv6.conf'
-
-default_config_data = {
- 'reboot_message': False,
- 'ipv6_forward': '1',
- 'disable_addr_assignment': False,
- 'mp_layer4_hashing': '0',
- 'neighbor_cache': 8192,
- 'strict_dad': '1'
-
-}
-
-def sysctl(name, value):
- call('sysctl -wq {}={}'.format(name, value))
-
def get_config(config=None):
- ip_opt = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
- conf.set_level('system ipv6')
- if conf.exists(''):
- ip_opt['disable_addr_assignment'] = conf.exists('disable')
- if conf.exists_effective('disable') != conf.exists('disable'):
- ip_opt['reboot_message'] = True
-
- if conf.exists('disable-forwarding'):
- ip_opt['ipv6_forward'] = '0'
+ base = ['system', 'ipv6']
- if conf.exists('multipath layer4-hashing'):
- ip_opt['mp_layer4_hashing'] = '1'
+ opt = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- if conf.exists('neighbor table-size'):
- ip_opt['neighbor_cache'] = int(conf.return_value('neighbor table-size'))
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ opt = dict_merge(default_values, opt)
- if conf.exists('strict-dad'):
- ip_opt['strict_dad'] = 2
+ return opt
- return ip_opt
-
-def verify(ip_opt):
+def verify(opt):
pass
-def generate(ip_opt):
+def generate(opt):
pass
-def apply(ip_opt):
- # disable IPv6 address assignment
- if ip_opt['disable_addr_assignment']:
- with open(ipv6_disable_file, 'w') as f:
- f.write('options ipv6 disable_ipv6=1')
- else:
- if os.path.exists(ipv6_disable_file):
- os.unlink(ipv6_disable_file)
-
- if ip_opt['reboot_message']:
- print('Changing IPv6 disable parameter will only take affect\n' \
- 'when the system is rebooted.')
-
+def apply(opt):
# configure multipath
- sysctl('net.ipv6.fib_multipath_hash_policy', ip_opt['mp_layer4_hashing'])
-
- # apply neighbor table threshold values
- sysctl('net.ipv6.neigh.default.gc_thresh3', ip_opt['neighbor_cache'])
- sysctl('net.ipv6.neigh.default.gc_thresh2', ip_opt['neighbor_cache'] // 2)
- sysctl('net.ipv6.neigh.default.gc_thresh1', ip_opt['neighbor_cache'] // 8)
+ tmp = dict_search('multipath.layer4_hashing', opt)
+ value = '1' if (tmp != None) else '0'
+ sysctl_write('net.ipv6.fib_multipath_hash_policy', value)
+
+ # Apply ND threshold values
+ # table_size has a default value - thus the key always exists
+ size = int(dict_search('neighbor.table_size', opt))
+ # Amount upon reaching which the records begin to be cleared immediately
+ sysctl_write('net.ipv6.neigh.default.gc_thresh3', size)
+ # Amount after which the records begin to be cleaned after 5 seconds
+ sysctl_write('net.ipv6.neigh.default.gc_thresh2', size // 2)
+ # Minimum number of stored records is indicated which is not cleared
+ sysctl_write('net.ipv6.neigh.default.gc_thresh1', size // 8)
# enable/disable IPv6 forwarding
- with open('/proc/sys/net/ipv6/conf/all/forwarding', 'w') as f:
- f.write(ip_opt['ipv6_forward'])
+ tmp = dict_search('disable_forwarding', opt)
+ value = '0' if (tmp != None) else '1'
+ write_file('/proc/sys/net/ipv6/conf/all/forwarding', value)
# configure IPv6 strict-dad
+ tmp = dict_search('strict_dad', opt)
+ value = '2' if (tmp != None) else '1'
for root, dirs, files in os.walk('/proc/sys/net/ipv6/conf'):
for name in files:
- if name == "accept_dad":
- with open(os.path.join(root, name), 'w') as f:
- f.write(str(ip_opt['strict_dad']))
+ if name == 'accept_dad':
+ write_file(os.path.join(root, name), value)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 4dd7f936d..c9c6aa187 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -23,6 +23,7 @@ from pwd import getpwall
from pwd import getpwnam
from spwd import getspnam
from sys import exit
+from time import sleep
from vyos.config import Config
from vyos.configdict import dict_merge
@@ -31,6 +32,7 @@ from vyos.template import render
from vyos.template import is_ipv4
from vyos.util import cmd
from vyos.util import call
+from vyos.util import run
from vyos.util import DEVNULL
from vyos.util import dict_search
from vyos.xml import defaults
@@ -250,13 +252,22 @@ def apply(login):
if 'rm_users' in login:
for user in login['rm_users']:
try:
+ # Disable user to prevent re-login
+ call(f'usermod -s /sbin/nologin {user}')
+
# Logout user if he is still logged in
if user in list(set([tmp[0] for tmp in users()])):
print(f'{user} is logged in, forcing logout!')
- call(f'pkill -HUP -u {user}')
-
- # Remove user account but leave home directory to be safe
- call(f'userdel --remove {user}', stderr=DEVNULL)
+ # re-run command until user is logged out
+ while run(f'pkill -HUP -u {user}'):
+ sleep(0.250)
+
+ # Remove user account but leave home directory in place. Re-run
+ # command until user is removed - userdel might return 8 as
+ # SSH sessions are not all yet properly cleaned away, thus we
+ # simply re-run the command until the account wen't away
+ while run(f'userdel --remove {user}', stderr=DEVNULL):
+ sleep(0.250)
except Exception as e:
raise ConfigError(f'Deleting user "{user}" raised exception: {e}')
diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py
index 3d8a51cd8..309b4bdb0 100755
--- a/src/conf_mode/system-syslog.py
+++ b/src/conf_mode/system-syslog.py
@@ -17,6 +17,7 @@
import os
import re
+from pathlib import Path
from sys import exit
from vyos.config import Config
@@ -89,7 +90,7 @@ def get_config(config=None):
filename: {
'log-file': '/var/log/user/' + filename,
'max-files': '5',
- 'action-on-max-size': '/usr/sbin/logrotate /etc/logrotate.d/' + filename,
+ 'action-on-max-size': '/usr/sbin/logrotate /etc/logrotate.d/vyos-rsyslog-generated-' + filename,
'selectors': '*.err',
'max-size': 262144
}
@@ -205,10 +206,17 @@ def generate(c):
conf = '/etc/rsyslog.d/vyos-rsyslog.conf'
render(conf, 'syslog/rsyslog.conf.tmpl', c)
+ # cleanup current logrotate config files
+ logrotate_files = Path('/etc/logrotate.d/').glob('vyos-rsyslog-generated-*')
+ for file in logrotate_files:
+ file.unlink()
+
# eventually write for each file its own logrotate file, since size is
# defined it shouldn't matter
- conf = '/etc/logrotate.d/vyos-rsyslog'
- render(conf, 'syslog/logrotate.tmpl', c)
+ for filename, fileconfig in c.get('files', {}).items():
+ if fileconfig['log-file'].startswith('/var/log/user/'):
+ conf = '/etc/logrotate.d/vyos-rsyslog-generated-' + filename
+ render(conf, 'syslog/logrotate.tmpl', { 'config_render': fileconfig })
def verify(c):
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 38c0c4463..f79c8a21e 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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,6 +29,7 @@ from vyos.util import dict_search
from vyos.util import get_interface_config
from vyos.util import popen
from vyos.util import run
+from vyos.util import sysctl_write
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -37,10 +38,16 @@ airbag.enable()
config_file = '/etc/iproute2/rt_tables.d/vyos-vrf.conf'
nft_vrf_config = '/tmp/nftables-vrf-zones'
-def list_rules():
- command = 'ip -j -4 rule show'
- answer = loads(cmd(command))
- return [_ for _ in answer if _]
+def has_rule(af : str, priority : int, table : str):
+ """ Check if a given ip rule exists """
+ if af not in ['-4', '-6']:
+ raise ValueError()
+ command = f'ip -j {af} rule show'
+ for tmp in loads(cmd(command)):
+ if {'priority', 'table'} <= set(tmp):
+ if tmp['priority'] == priority and tmp['table'] == table:
+ return True
+ return False
def vrf_interfaces(c, match):
matched = []
@@ -69,7 +76,6 @@ def vrf_routing(c, match):
c.set_level(old_level)
return matched
-
def get_config(config=None):
if config:
conf = config
@@ -148,13 +154,11 @@ def apply(vrf):
bind_all = '0'
if 'bind-to-all' in vrf:
bind_all = '1'
- call(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}')
- call(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}')
+ sysctl_write('net.ipv4.tcp_l3mdev_accept', bind_all)
+ sysctl_write('net.ipv4.udp_l3mdev_accept', bind_all)
for tmp in (dict_search('vrf_remove', vrf) or []):
if os.path.isdir(f'/sys/class/net/{tmp}'):
- call(f'ip -4 route del vrf {tmp} unreachable default metric 4278198272')
- call(f'ip -6 route del vrf {tmp} unreachable default metric 4278198272')
call(f'ip link delete dev {tmp}')
# Remove nftables conntrack zone map item
nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}'
@@ -165,31 +169,59 @@ def apply(vrf):
# check if table already exists
_, err = popen('nft list table inet vrf_zones')
# If not, create a table
- if err:
- if os.path.exists(nft_vrf_config):
- cmd(f'nft -f {nft_vrf_config}')
- os.unlink(nft_vrf_config)
+ if err and os.path.exists(nft_vrf_config):
+ cmd(f'nft -f {nft_vrf_config}')
+ os.unlink(nft_vrf_config)
+
+ # Linux routing uses rules to find tables - routing targets are then
+ # looked up in those tables. If the lookup got a matching route, the
+ # process ends.
+ #
+ # TL;DR; first table with a matching entry wins!
+ #
+ # You can see your routing table lookup rules using "ip rule", sadly the
+ # local lookup is hit before any VRF lookup. Pinging an addresses from the
+ # VRF will usually find a hit in the local table, and never reach the VRF
+ # routing table - this is usually not what you want. Thus we will
+ # re-arrange the tables and move the local lookup further down once VRFs
+ # are enabled.
+ #
+ # Thanks to https://stbuehler.de/blog/article/2020/02/29/using_vrf__virtual_routing_and_forwarding__on_linux.html
+
+ for afi in ['-4', '-6']:
+ # move lookup local to pref 32765 (from 0)
+ if not has_rule(afi, 32765, 'local'):
+ call(f'ip {afi} rule add pref 32765 table local')
+ if has_rule(afi, 0, 'local'):
+ call(f'ip {afi} rule del pref 0')
+ # make sure that in VRFs after failed lookup in the VRF specific table
+ # nothing else is reached
+ if not has_rule(afi, 1000, 'l3mdev'):
+ # this should be added by the kernel when a VRF is created
+ # add it here for completeness
+ call(f'ip {afi} rule add pref 1000 l3mdev protocol kernel')
+
+ # add another rule with an unreachable target which only triggers in VRF context
+ # if a route could not be reached
+ if not has_rule(afi, 2000, 'l3mdev'):
+ call(f'ip {afi} rule add pref 2000 l3mdev unreachable')
for name, config in vrf['name'].items():
table = config['table']
-
if not os.path.isdir(f'/sys/class/net/{name}'):
# For each VRF apart from your default context create a VRF
# interface with a separate routing table
call(f'ip link add {name} type vrf table {table}')
- # The kernel Documentation/networking/vrf.txt also recommends
- # adding unreachable routes to the VRF routing tables so that routes
- # afterwards are taken.
- call(f'ip -4 route add vrf {name} unreachable default metric 4278198272')
- call(f'ip -6 route add vrf {name} unreachable default metric 4278198272')
- # We also should add proper loopback IP addresses to the newly
- # created VRFs for services bound to the loopback address (SNMP, NTP)
- call(f'ip -4 addr add 127.0.0.1/8 dev {name}')
- call(f'ip -6 addr add ::1/128 dev {name}')
# set VRF description for e.g. SNMP monitoring
vrf_if = Interface(name)
+ # We also should add proper loopback IP addresses to the newly added
+ # VRF for services bound to the loopback address (SNMP, NTP)
+ vrf_if.add_addr('127.0.0.1/8')
+ vrf_if.add_addr('::1/128')
+ # add VRF description if available
vrf_if.set_alias(config.get('description', ''))
+
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
@@ -203,37 +235,9 @@ def apply(vrf):
nft_add_element = f'add element inet vrf_zones ct_iface_map {{ "{name}" : {table} }}'
cmd(f'nft {nft_add_element}')
- # Linux routing uses rules to find tables - routing targets are then
- # looked up in those tables. If the lookup got a matching route, the
- # process ends.
- #
- # TL;DR; first table with a matching entry wins!
- #
- # You can see your routing table lookup rules using "ip rule", sadly the
- # local lookup is hit before any VRF lookup. Pinging an addresses from the
- # VRF will usually find a hit in the local table, and never reach the VRF
- # routing table - this is usually not what you want. Thus we will
- # re-arrange the tables and move the local lookup furhter down once VRFs
- # are enabled.
-
- # get current preference on local table
- local_pref = [r.get('priority') for r in list_rules() if r.get('table') == 'local'][0]
-
- # change preference when VRFs are enabled and local lookup table is default
- if not local_pref and 'name' in vrf:
- for af in ['-4', '-6']:
- call(f'ip {af} rule add pref 32765 table local')
- call(f'ip {af} rule del pref 0')
# return to default lookup preference when no VRF is configured
if 'name' not in vrf:
- for af in ['-4', '-6']:
- call(f'ip {af} rule add pref 0 table local')
- call(f'ip {af} rule del pref 32765')
-
- # clean out l3mdev-table rule if present
- if 1000 in [r.get('priority') for r in list_rules() if r.get('priority') == 1000]:
- call(f'ip {af} rule del pref 1000')
# Remove VRF zones table from nftables
tmp = run('nft list table inet vrf_zones')
if tmp == 0:
diff --git a/src/conf_mode/zone_policy.py b/src/conf_mode/zone_policy.py
index 683f8f034..dc0617353 100755
--- a/src/conf_mode/zone_policy.py
+++ b/src/conf_mode/zone_policy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -20,10 +20,12 @@ from json import loads
from sys import exit
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.template import render
from vyos.util import cmd
from vyos.util import dict_search_args
from vyos.util import run
+from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -36,12 +38,22 @@ def get_config(config=None):
else:
conf = Config()
base = ['zone-policy']
- zone_policy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
+ zone_policy = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
- if zone_policy:
- zone_policy['firewall'] = conf.get_config_dict(['firewall'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
+ zone_policy['firewall'] = conf.get_config_dict(['firewall'],
+ key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ if 'zone' in zone_policy:
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base + ['zone'])
+ for zone in zone_policy['zone']:
+ zone_policy['zone'][zone] = dict_merge(default_values,
+ zone_policy['zone'][zone])
return zone_policy
diff --git a/src/etc/cron.d/check-wwan b/src/etc/cron.d/check-wwan
deleted file mode 100644
index 28190776f..000000000
--- a/src/etc/cron.d/check-wwan
+++ /dev/null
@@ -1 +0,0 @@
-*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py
diff --git a/src/etc/logrotate.d/conntrackd b/src/etc/logrotate.d/conntrackd
new file mode 100644
index 000000000..b0b09dec1
--- /dev/null
+++ b/src/etc/logrotate.d/conntrackd
@@ -0,0 +1,9 @@
+/var/log/conntrackd-stats.log {
+ weekly
+ rotate 2
+ missingok
+
+ postrotate
+ systemctl restart conntrackd.service > /dev/null
+ endscript
+}
diff --git a/src/etc/logrotate.d/vyos-rsyslog b/src/etc/logrotate.d/vyos-rsyslog
new file mode 100644
index 000000000..3c087b94e
--- /dev/null
+++ b/src/etc/logrotate.d/vyos-rsyslog
@@ -0,0 +1,12 @@
+/var/log/messages {
+ create
+ missingok
+ nomail
+ notifempty
+ rotate 10
+ size 1M
+ postrotate
+ # inform rsyslog service about rotation
+ /usr/lib/rsyslog/rsyslog-rotate
+ endscript
+}
diff --git a/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
new file mode 100755
index 000000000..bf4bfd05d
--- /dev/null
+++ b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+
+import json
+import re
+import time
+
+from vyos.util import cmd
+
+
+def get_nft_filter_chains():
+ """
+ Get list of nft chains for table filter
+ """
+ nft = cmd('/usr/sbin/nft --json list table ip filter')
+ nft = json.loads(nft)
+ chain_list = []
+
+ for output in nft['nftables']:
+ if 'chain' in output:
+ chain = output['chain']['name']
+ chain_list.append(chain)
+
+ return chain_list
+
+
+def get_nftables_details(name):
+ """
+ Get dict, counters packets and bytes for chain
+ """
+ command = f'/usr/sbin/nft list chain ip filter {name}'
+ try:
+ results = cmd(command)
+ except:
+ return {}
+
+ # Trick to remove 'NAME_' from chain name in the comment
+ # It was added to any chain T4218
+ # counter packets 0 bytes 0 return comment "FOO default-action accept"
+ comment_name = name.replace("NAME_", "")
+ out = {}
+ for line in results.split('\n'):
+ comment_search = re.search(rf'{comment_name}[\- ](\d+|default-action)', line)
+ if not comment_search:
+ continue
+
+ rule = {}
+ rule_id = comment_search[1]
+ counter_search = re.search(r'counter packets (\d+) bytes (\d+)', line)
+ if counter_search:
+ rule['packets'] = counter_search[1]
+ rule['bytes'] = counter_search[2]
+
+ rule['conditions'] = re.sub(r'(\b(counter packets \d+ bytes \d+|drop|reject|return|log)\b|comment "[\w\-]+")', '', line).strip()
+ out[rule_id] = rule
+ return out
+
+
+def get_nft_telegraf(name):
+ """
+ Get data for telegraf in influxDB format
+ """
+ for rule, rule_config in get_nftables_details(name).items():
+ print(f'nftables,table=filter,chain={name},'
+ f'ruleid={rule} '
+ f'pkts={rule_config["packets"]}i,'
+ f'bytes={rule_config["bytes"]}i '
+ f'{str(int(time.time()))}000000000')
+
+
+chains = get_nft_filter_chains()
+
+for chain in chains:
+ get_nft_telegraf(chain)
diff --git a/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py b/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py
index 0f5e366cd..0c7474156 100755
--- a/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py
+++ b/src/etc/telegraf/custom_scripts/show_interfaces_input_filter.py
@@ -1,47 +1,88 @@
#!/usr/bin/env python3
-import subprocess
+from vyos.ifconfig import Section
+from vyos.ifconfig import Interface
+
import time
-def status_to_int(status):
- switcher={
- 'u':'0',
- 'D':'1',
- 'A':'2'
- }
- return switcher.get(status,"")
-
-def description_check(line):
- desc=" ".join(line[3:])
- if desc == "":
+def get_interfaces(type='', vlan=True):
+ """
+ Get interfaces:
+ ['dum0', 'eth0', 'eth1', 'eth1.5', 'lo', 'tun0']
+ """
+ interfaces = []
+ ifaces = Section.interfaces(type)
+ for iface in ifaces:
+ if vlan == False and '.' in iface:
+ continue
+ interfaces.append(iface)
+
+ return interfaces
+
+def get_interface_addresses(iface, link_local_v6=False):
+ """
+ Get IP and IPv6 addresses from interface in one string
+ By default don't get IPv6 link-local addresses
+ If interface doesn't have address, return "-"
+ """
+ addresses = []
+ addrs = Interface(iface).get_addr()
+
+ for addr in addrs:
+ if link_local_v6 == False:
+ if addr.startswith('fe80::'):
+ continue
+ addresses.append(addr)
+
+ if not addresses:
+ return "-"
+
+ return (" ".join(addresses))
+
+def get_interface_description(iface):
+ """
+ Get interface description
+ If none return "empty"
+ """
+ description = Interface(iface).get_alias()
+
+ if not description:
return "empty"
+
+ return description
+
+def get_interface_admin_state(iface):
+ """
+ Interface administrative state
+ up => 0, down => 2
+ """
+ state = Interface(iface).get_admin_state()
+ if state == 'up':
+ admin_state = 0
+ if state == 'down':
+ admin_state = 2
+
+ return admin_state
+
+def get_interface_oper_state(iface):
+ """
+ Interface operational state
+ up => 0, down => 1
+ """
+ state = Interface(iface).operational.get_state()
+ if state == 'down':
+ oper_state = 1
else:
- return desc
-
-def gen_ip_list(index,interfaces):
- line=interfaces[index].split()
- ip_list=line[1]
- if index < len(interfaces):
- index += 1
- while len(interfaces[index].split())==1:
- ip = interfaces[index].split()
- ip_list = ip_list + " " + ip[0]
- index += 1
- if index == len(interfaces):
- break
- return ip_list
-
-interfaces = subprocess.check_output("/usr/libexec/vyos/op_mode/show_interfaces.py --action=show-brief", shell=True).decode('utf-8').splitlines()
-del interfaces[:3]
-lines_count=len(interfaces)
-index=0
-while index<lines_count:
- line=interfaces[index].split()
- if len(line)>1:
- print(f'show_interfaces,interface={line[0]} '
- f'ip_addresses="{gen_ip_list(index,interfaces)}",'
- f'state={status_to_int(line[2][0])}i,'
- f'link={status_to_int(line[2][2])}i,'
- f'description="{description_check(line)}" '
- f'{str(int(time.time()))}000000000')
- index += 1
+ oper_state = 0
+
+ return oper_state
+
+interfaces = get_interfaces()
+
+for iface in interfaces:
+ print(f'show_interfaces,interface={iface} '
+ f'ip_addresses="{get_interface_addresses(iface)}",'
+ f'state={get_interface_admin_state(iface)}i,'
+ f'link={get_interface_oper_state(iface)}i,'
+ f'description="{get_interface_description(iface)}" '
+ f'{str(int(time.time()))}000000000')
diff --git a/src/helpers/strip-private.py b/src/helpers/strip-private.py
index e4e1fe11d..eb584edaf 100755
--- a/src/helpers/strip-private.py
+++ b/src/helpers/strip-private.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2022 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
@@ -111,6 +111,10 @@ if __name__ == "__main__":
(True, re.compile(r'public-keys \S+'), 'public-keys xxxx@xxx.xxx'),
(True, re.compile(r'type \'ssh-(rsa|dss)\''), 'type ssh-xxx'),
(True, re.compile(r' key \S+'), ' key xxxxxx'),
+ # Strip bucket
+ (True, re.compile(r' bucket \S+'), ' bucket xxxxxx'),
+ # Strip tokens
+ (True, re.compile(r' token \S+'), ' token xxxxxx'),
# Strip OpenVPN secrets
(True, re.compile(r'(shared-secret-key-file|ca-cert-file|cert-file|dh-file|key-file|client) (\S+)'), r'\1 xxxxxx'),
# Strip IPSEC secrets
@@ -123,8 +127,8 @@ if __name__ == "__main__":
# Strip MAC addresses
(args.mac, re.compile(r'([0-9a-fA-F]{2}\:){5}([0-9a-fA-F]{2}((\:{0,1})){3})'), r'xx:xx:xx:xx:xx:\2'),
- # Strip host-name, domain-name, and domain-search
- (args.hostname, re.compile(r'(host-name|domain-name|domain-search) \S+'), r'\1 xxxxxx'),
+ # Strip host-name, domain-name, domain-search and url
+ (args.hostname, re.compile(r'(host-name|domain-name|domain-search|url) \S+'), r'\1 xxxxxx'),
# Strip user-names
(args.username, re.compile(r'(user|username|user-id) \S+'), r'\1 xxxxxx'),
diff --git a/src/helpers/system-versions-foot.py b/src/helpers/system-versions-foot.py
index c33e41d79..2aa687221 100755
--- a/src/helpers/system-versions-foot.py
+++ b/src/helpers/system-versions-foot.py
@@ -21,7 +21,7 @@ import vyos.systemversions as systemversions
import vyos.defaults
import vyos.version
-sys_versions = systemversions.get_system_versions()
+sys_versions = systemversions.get_system_component_version()
component_string = formatversions.format_versions_string(sys_versions)
diff --git a/src/helpers/vyos-vrrp-conntracksync.sh b/src/helpers/vyos-vrrp-conntracksync.sh
index 4501aa63e..0cc718938 100755
--- a/src/helpers/vyos-vrrp-conntracksync.sh
+++ b/src/helpers/vyos-vrrp-conntracksync.sh
@@ -14,12 +14,10 @@
# Modified by : Mohit Mehta <mohit@vyatta.com>
# Slight modifications were made to this script for running with Vyatta
# The original script came from 0.9.14 debian conntrack-tools package
-#
-#
CONNTRACKD_BIN=/usr/sbin/conntrackd
CONNTRACKD_LOCK=/var/lock/conntrack.lock
-CONNTRACKD_CONFIG=/etc/conntrackd/conntrackd.conf
+CONNTRACKD_CONFIG=/run/conntrackd/conntrackd.conf
FACILITY=daemon
LEVEL=notice
TAG=conntrack-tools
@@ -29,6 +27,10 @@ FAILOVER_STATE="/var/run/vyatta-conntrackd-failover-state"
$LOGCMD "vyatta-vrrp-conntracksync invoked at `date`"
+if ! systemctl is-active --quiet conntrackd.service; then
+ echo "conntrackd service not running"
+ exit 1
+fi
if [ ! -e $FAILOVER_STATE ]; then
mkdir -p /var/run
diff --git a/src/helpers/vyos_net_name b/src/helpers/vyos_net_name
index afeef8f2d..1798e92db 100755
--- a/src/helpers/vyos_net_name
+++ b/src/helpers/vyos_net_name
@@ -20,12 +20,14 @@ import os
import re
import time
import logging
+import tempfile
import threading
from sys import argv
from vyos.configtree import ConfigTree
from vyos.defaults import directories
from vyos.util import cmd, boot_configuration_complete
+from vyos.migrator import VirtualMigrator
vyos_udev_dir = directories['vyos_udev_dir']
vyos_log_dir = '/run/udev/log'
@@ -139,14 +141,20 @@ def get_configfile_interfaces() -> dict:
try:
config = ConfigTree(config_file)
except Exception:
- logging.debug(f"updating component version string syntax")
try:
- # this will update the component version string in place, for
- # updates 1.2 --> 1.3/1.4
- os.system(f'/usr/libexec/vyos/run-config-migration.py {config_path} --virtual --set-vintage=vyos')
- with open(config_path) as f:
- config_file = f.read()
+ logging.debug(f"updating component version string syntax")
+ # this will update the component version string syntax,
+ # required for updates 1.2 --> 1.3/1.4
+ with tempfile.NamedTemporaryFile() as fp:
+ with open(fp.name, 'w') as fd:
+ fd.write(config_file)
+ virtual_migration = VirtualMigrator(fp.name)
+ virtual_migration.run()
+ with open(fp.name) as fd:
+ config_file = fd.read()
+
config = ConfigTree(config_file)
+
except Exception as e:
logging.critical(f"ConfigTree error: {e}")
@@ -246,4 +254,3 @@ if not boot_configuration_complete():
else:
logging.debug("boot configuration complete")
lock.release()
-
diff --git a/src/migration-scripts/bgp/0-to-1 b/src/migration-scripts/bgp/0-to-1
index b1d5a6514..5e9dffe1f 100755
--- a/src/migration-scripts/bgp/0-to-1
+++ b/src/migration-scripts/bgp/0-to-1
@@ -33,7 +33,7 @@ with open(file_name, 'r') as f:
base = ['protocols', 'bgp']
config = ConfigTree(config_file)
-if not config.exists(base):
+if not config.exists(base) or not config.is_tag(base):
# Nothing to do
exit(0)
diff --git a/src/migration-scripts/bgp/1-to-2 b/src/migration-scripts/bgp/1-to-2
index 4c6d5ceb8..e2d3fcd33 100755
--- a/src/migration-scripts/bgp/1-to-2
+++ b/src/migration-scripts/bgp/1-to-2
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -20,7 +20,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.template import is_ipv4
if (len(argv) < 1):
print("Must specify file name!")
@@ -51,23 +50,21 @@ if config.exists(base + ['parameters', 'default', 'no-ipv4-unicast']):
# Check if the "default" node is now empty, if so - remove it
if len(config.list_nodes(base + ['parameters'])) == 0:
config.delete(base + ['parameters'])
+else:
+ # As we now install a new default option into BGP we need to migrate all
+ # existing BGP neighbors and restore the old behavior
+ if config.exists(base + ['neighbor']):
+ for neighbor in config.list_nodes(base + ['neighbor']):
+ peer_group = base + ['neighbor', neighbor, 'peer-group']
+ if config.exists(peer_group):
+ peer_group_name = config.return_value(peer_group)
+ # peer group enables old behavior for neighbor - bail out
+ if config.exists(base + ['peer-group', peer_group_name, 'address-family', 'ipv4-unicast']):
+ continue
- exit(0)
-
-# As we now install a new default option into BGP we need to migrate all
-# existing BGP neighbors and restore the old behavior
-if config.exists(base + ['neighbor']):
- for neighbor in config.list_nodes(base + ['neighbor']):
- peer_group = base + ['neighbor', neighbor, 'peer-group']
- if config.exists(peer_group):
- peer_group_name = config.return_value(peer_group)
- # peer group enables old behavior for neighbor - bail out
- if config.exists(base + ['peer-group', peer_group_name, 'address-family', 'ipv4-unicast']):
- continue
-
- afi_ipv4 = base + ['neighbor', neighbor, 'address-family', 'ipv4-unicast']
- if not config.exists(afi_ipv4):
- config.set(afi_ipv4)
+ afi_ipv4 = base + ['neighbor', neighbor, 'address-family', 'ipv4-unicast']
+ if not config.exists(afi_ipv4):
+ config.set(afi_ipv4)
try:
with open(file_name, 'w') as f:
diff --git a/src/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2
index ba10c26f2..a8c930be7 100755
--- a/src/migration-scripts/dns-forwarding/1-to-2
+++ b/src/migration-scripts/dns-forwarding/1-to-2
@@ -16,7 +16,7 @@
#
# This migration script will remove the deprecated 'listen-on' statement
-# from the dns forwarding service and will add the corresponding
+# from the dns forwarding service and will add the corresponding
# listen-address nodes instead. This is required as PowerDNS can only listen
# on interface addresses and not on interface names.
@@ -37,53 +37,50 @@ with open(file_name, 'r') as f:
config = ConfigTree(config_file)
base = ['service', 'dns', 'forwarding']
-if not config.exists(base):
+if not config.exists(base + ['listen-on']):
# Nothing to do
exit(0)
-if config.exists(base + ['listen-on']):
- listen_intf = config.return_values(base + ['listen-on'])
- # Delete node with abandoned command
- config.delete(base + ['listen-on'])
+listen_intf = config.return_values(base + ['listen-on'])
+# Delete node with abandoned command
+config.delete(base + ['listen-on'])
- # retrieve interface addresses for every configured listen-on interface
- listen_addr = []
- for intf in listen_intf:
- # we need to evaluate the interface section before manipulating the 'intf' variable
- section = Interface.section(intf)
- if not section:
- raise ValueError(f'Invalid interface name {intf}')
+# retrieve interface addresses for every configured listen-on interface
+listen_addr = []
+for intf in listen_intf:
+ # we need to evaluate the interface section before manipulating the 'intf' variable
+ section = Interface.section(intf)
+ if not section:
+ raise ValueError(f'Invalid interface name {intf}')
- # we need to treat vif and vif-s interfaces differently,
- # both "real interfaces" use dots for vlan identifiers - those
- # need to be exchanged with vif and vif-s identifiers
- if intf.count('.') == 1:
- # this is a regular VLAN interface
- intf = intf.split('.')[0] + ' vif ' + intf.split('.')[1]
- elif intf.count('.') == 2:
- # this is a QinQ VLAN interface
- intf = intf.split('.')[0] + ' vif-s ' + intf.split('.')[1] + ' vif-c ' + intf.split('.')[2]
-
- # retrieve corresponding interface addresses in CIDR format
- # those need to be converted in pure IP addresses without network information
- path = ['interfaces', section, intf, 'address']
- 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)
+ # we need to treat vif and vif-s interfaces differently,
+ # both "real interfaces" use dots for vlan identifiers - those
+ # need to be exchanged with vif and vif-s identifiers
+ if intf.count('.') == 1:
+ # this is a regular VLAN interface
+ intf = intf.split('.')[0] + ' vif ' + intf.split('.')[1]
+ elif intf.count('.') == 2:
+ # this is a QinQ VLAN interface
+ intf = intf.split('.')[0] + ' vif-s ' + intf.split('.')[1] + ' vif-c ' + intf.split('.')[2]
+ # retrieve corresponding interface addresses in CIDR format
+ # those need to be converted in pure IP addresses without network information
+ path = ['interfaces', section, intf, 'address']
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)
+ 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")
-exit(0)
+for addr in listen_addr:
+ config.set(base + ['listen-address'], value=addr, replace=False)
+
+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/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7
index 4a4097d56..5f4cff90d 100755
--- a/src/migration-scripts/firewall/6-to-7
+++ b/src/migration-scripts/firewall/6-to-7
@@ -17,6 +17,10 @@
# T2199: Remove unavailable nodes due to XML/Python implementation using nftables
# monthdays: nftables does not have a monthdays equivalent
# utc: nftables userspace uses localtime and calculates the UTC offset automatically
+# icmp/v6: migrate previously available `type-name` to valid type/code
+# T4178: Update tcp flags to use multi value node
+
+import re
from sys import argv
from sys import exit
@@ -40,29 +44,179 @@ if not config.exists(base):
# Nothing to do
exit(0)
+icmp_remove = ['any']
+icmp_translations = {
+ 'ping': 'echo-request',
+ 'pong': 'echo-reply',
+ 'ttl-exceeded': 'time-exceeded',
+ # Network Unreachable
+ 'network-unreachable': [3, 0],
+ 'host-unreachable': [3, 1],
+ 'protocol-unreachable': [3, 2],
+ 'port-unreachable': [3, 3],
+ 'fragmentation-needed': [3, 4],
+ 'source-route-failed': [3, 5],
+ 'network-unknown': [3, 6],
+ 'host-unknown': [3, 7],
+ 'network-prohibited': [3, 9],
+ 'host-prohibited': [3, 10],
+ 'TOS-network-unreachable': [3, 11],
+ 'TOS-host-unreachable': [3, 12],
+ 'communication-prohibited': [3, 13],
+ 'host-precedence-violation': [3, 14],
+ 'precedence-cutoff': [3, 15],
+ # Redirect
+ 'network-redirect': [5, 0],
+ 'host-redirect': [5, 1],
+ 'TOS-network-redirect': [5, 2],
+ 'TOS host-redirect': [5, 3],
+ # Time Exceeded
+ 'ttl-zero-during-transit': [11, 0],
+ 'ttl-zero-during-reassembly': [11, 1],
+ # Parameter Problem
+ 'ip-header-bad': [12, 0],
+ 'required-option-missing': [12, 1]
+}
+
+icmpv6_remove = []
+icmpv6_translations = {
+ 'ping': 'echo-request',
+ 'pong': 'echo-reply',
+ # Destination Unreachable
+ 'no-route': [1, 0],
+ 'communication-prohibited': [1, 1],
+ 'address-unreachble': [1, 3],
+ 'port-unreachable': [1, 4],
+ # Redirect
+ 'redirect': 'nd-redirect',
+ # Time Exceeded
+ 'ttl-zero-during-transit': [3, 0],
+ 'ttl-zero-during-reassembly': [3, 1],
+ # Parameter Problem
+ 'bad-header': [4, 0],
+ 'unknown-header-type': [4, 1],
+ 'unknown-option': [4, 2]
+}
+
if config.exists(base + ['name']):
for name in config.list_nodes(base + ['name']):
- if config.exists(base + ['name', name, 'rule']):
- for rule in config.list_nodes(base + ['name', name, 'rule']):
- rule_time = base + ['name', name, 'rule', rule, 'time']
+ if not config.exists(base + ['name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['name', name, 'rule']):
+ rule_recent = base + ['name', name, 'rule', rule, 'recent']
+ rule_time = base + ['name', name, 'rule', rule, 'time']
+ rule_tcp_flags = base + ['name', name, 'rule', rule, 'tcp', 'flags']
+ rule_icmp = base + ['name', name, 'rule', rule, 'icmp']
+
+ if config.exists(rule_time + ['monthdays']):
+ config.delete(rule_time + ['monthdays'])
+
+ if config.exists(rule_time + ['utc']):
+ config.delete(rule_time + ['utc'])
+
+ if config.exists(rule_recent + ['time']):
+ tmp = int(config.return_value(rule_recent + ['time']))
+ unit = 'minute'
+ if tmp > 600:
+ unit = 'hour'
+ elif tmp < 10:
+ unit = 'second'
+ config.set(rule_recent + ['time'], value=unit)
+
+ if config.exists(rule_tcp_flags):
+ tmp = config.return_value(rule_tcp_flags)
+ config.delete(rule_tcp_flags)
+ for flag in tmp.split(","):
+ if flag[0] == '!':
+ config.set(rule_tcp_flags + ['not', flag[1:].lower()])
+ else:
+ config.set(rule_tcp_flags + [flag.lower()])
- if config.exists(rule_time + ['monthdays']):
- config.delete(rule_time + ['monthdays'])
+ if config.exists(rule_icmp + ['type-name']):
+ tmp = config.return_value(rule_icmp + ['type-name'])
+ if tmp in icmp_remove:
+ config.delete(rule_icmp + ['type-name'])
+ elif tmp in icmp_translations:
+ translate = icmp_translations[tmp]
+ if isinstance(translate, str):
+ config.set(rule_icmp + ['type-name'], value=translate)
+ elif isinstance(translate, list):
+ config.delete(rule_icmp + ['type-name'])
+ config.set(rule_icmp + ['type'], value=translate[0])
+ config.set(rule_icmp + ['code'], value=translate[1])
- if config.exists(rule_time + ['utc']):
- config.delete(rule_time + ['utc'])
+ for src_dst in ['destination', 'source']:
+ pg_base = base + ['name', name, 'rule', rule, src_dst, 'group', 'port-group']
+ proto_base = base + ['name', name, 'rule', rule, 'protocol']
+ if config.exists(pg_base) and not config.exists(proto_base):
+ config.set(proto_base, value='tcp_udp')
if config.exists(base + ['ipv6-name']):
for name in config.list_nodes(base + ['ipv6-name']):
- if config.exists(base + ['ipv6-name', name, 'rule']):
- for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
- rule_time = base + ['ipv6-name', name, 'rule', rule, 'time']
+ if not config.exists(base + ['ipv6-name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
+ rule_recent = base + ['ipv6-name', name, 'rule', rule, 'recent']
+ rule_time = base + ['ipv6-name', name, 'rule', rule, 'time']
+ rule_tcp_flags = base + ['ipv6-name', name, 'rule', rule, 'tcp', 'flags']
+ rule_icmp = base + ['ipv6-name', name, 'rule', rule, 'icmpv6']
+
+ if config.exists(rule_time + ['monthdays']):
+ config.delete(rule_time + ['monthdays'])
+
+ if config.exists(rule_time + ['utc']):
+ config.delete(rule_time + ['utc'])
+
+ if config.exists(rule_recent + ['time']):
+ tmp = int(config.return_value(rule_recent + ['time']))
+ unit = 'minute'
+ if tmp > 600:
+ unit = 'hour'
+ elif tmp < 10:
+ unit = 'second'
+ config.set(rule_recent + ['time'], value=unit)
+
+ if config.exists(rule_tcp_flags):
+ tmp = config.return_value(rule_tcp_flags)
+ config.delete(rule_tcp_flags)
+ for flag in tmp.split(","):
+ if flag[0] == '!':
+ config.set(rule_tcp_flags + ['not', flag[1:].lower()])
+ else:
+ config.set(rule_tcp_flags + [flag.lower()])
+
+ if config.exists(base + ['ipv6-name', name, 'rule', rule, 'protocol']):
+ tmp = config.return_value(base + ['ipv6-name', name, 'rule', rule, 'protocol'])
+ if tmp == 'icmpv6':
+ config.set(base + ['ipv6-name', name, 'rule', rule, 'protocol'], value='ipv6-icmp')
+
+ if config.exists(rule_icmp + ['type']):
+ tmp = config.return_value(rule_icmp + ['type'])
+ type_code_match = re.match(r'^(\d+)/(\d+)$', tmp)
- if config.exists(rule_time + ['monthdays']):
- config.delete(rule_time + ['monthdays'])
+ if type_code_match:
+ config.set(rule_icmp + ['type'], value=type_code_match[1])
+ config.set(rule_icmp + ['code'], value=type_code_match[2])
+ elif tmp in icmpv6_remove:
+ config.delete(rule_icmp + ['type'])
+ elif tmp in icmpv6_translations:
+ translate = icmpv6_translations[tmp]
+ if isinstance(translate, str):
+ config.delete(rule_icmp + ['type'])
+ config.set(rule_icmp + ['type-name'], value=translate)
+ elif isinstance(translate, list):
+ config.set(rule_icmp + ['type'], value=translate[0])
+ config.set(rule_icmp + ['code'], value=translate[1])
+ else:
+ config.rename(rule_icmp + ['type'], 'type-name')
- if config.exists(rule_time + ['utc']):
- config.delete(rule_time + ['utc'])
+ for src_dst in ['destination', 'source']:
+ pg_base = base + ['ipv6-name', name, 'rule', rule, src_dst, 'group', 'port-group']
+ proto_base = base + ['ipv6-name', name, 'rule', rule, 'protocol']
+ if config.exists(pg_base) and not config.exists(proto_base):
+ config.set(proto_base, value='tcp_udp')
try:
with open(file_name, 'w') as f:
diff --git a/src/migration-scripts/ipsec/8-to-9 b/src/migration-scripts/ipsec/8-to-9
new file mode 100755
index 000000000..eb44b6216
--- /dev/null
+++ b/src/migration-scripts/ipsec/8-to-9
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 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 = ['vpn', 'ipsec', 'ike-group']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+else:
+ for ike_group in config.list_nodes(base):
+ base_closeaction = base + [ike_group, 'close-action']
+ if config.exists(base_closeaction) and config.return_value(base_closeaction) == 'clear':
+ config.set(base_closeaction, 'none', replace=True)
+
+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/policy/1-to-2 b/src/migration-scripts/policy/1-to-2
new file mode 100755
index 000000000..eebbf9d41
--- /dev/null
+++ b/src/migration-scripts/policy/1-to-2
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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/>.
+
+# T4170: rename "policy ipv6-route" to "policy route6" to match common
+# IPv4/IPv6 schema
+# T4178: Update tcp flags to use multi value node
+
+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 = ['policy', 'ipv6-route']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+config.rename(base, 'route6')
+config.set_tag(['policy', 'route6'])
+
+for route in ['route', 'route6']:
+ route_path = ['policy', route]
+ if config.exists(route_path):
+ for name in config.list_nodes(route_path):
+ if config.exists(route_path + [name, 'rule']):
+ for rule in config.list_nodes(route_path + [name, 'rule']):
+ rule_tcp_flags = route_path + [name, 'rule', rule, 'tcp', 'flags']
+
+ if config.exists(rule_tcp_flags):
+ tmp = config.return_value(rule_tcp_flags)
+ config.delete(rule_tcp_flags)
+ for flag in tmp.split(","):
+ for flag in tmp.split(","):
+ if flag[0] == '!':
+ config.set(rule_tcp_flags + ['not', flag[1:].lower()])
+ else:
+ config.set(rule_tcp_flags + [flag.lower()])
+
+if config.exists(['interfaces']):
+ def if_policy_rename(config, path):
+ if config.exists(path + ['policy', 'ipv6-route']):
+ config.rename(path + ['policy', 'ipv6-route'], 'route6')
+
+ for if_type in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', if_type]):
+ if_path = ['interfaces', if_type, ifname]
+ if_policy_rename(config, if_path)
+
+ for vif_type in ['vif', 'vif-s']:
+ if config.exists(if_path + [vif_type]):
+ for vifname in config.list_nodes(if_path + [vif_type]):
+ if_policy_rename(config, if_path + [vif_type, vifname])
+
+ if config.exists(if_path + [vif_type, vifname, 'vif-c']):
+ for vifcname in config.list_nodes(if_path + [vif_type, vifname, 'vif-c']):
+ if_policy_rename(config, if_path + [vif_type, vifname, 'vif-c', vifcname])
+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/ssh/1-to-2 b/src/migration-scripts/ssh/1-to-2
index bc8815753..31c40df16 100755
--- a/src/migration-scripts/ssh/1-to-2
+++ b/src/migration-scripts/ssh/1-to-2
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -30,26 +30,52 @@ file_name = argv[1]
with open(file_name, 'r') as f:
config_file = f.read()
-base = ['service', 'ssh', 'loglevel']
+base = ['service', 'ssh']
config = ConfigTree(config_file)
if not config.exists(base):
# Nothing to do
exit(0)
-else:
- # red in configured loglevel and convert it to lower case
- tmp = config.return_value(base).lower()
+path_loglevel = base + ['loglevel']
+if config.exists(path_loglevel):
+ # red in configured loglevel and convert it to lower case
+ tmp = config.return_value(path_loglevel).lower()
# VyOS 1.2 had no proper value validation on the CLI thus the
# user could use any arbitrary values - sanitize them
if tmp not in ['quiet', 'fatal', 'error', 'info', 'verbose']:
tmp = 'info'
+ config.set(path_loglevel, value=tmp)
+
+# T4273: migrate ssh cipher list to multi node
+path_ciphers = base + ['ciphers']
+if config.exists(path_ciphers):
+ tmp = []
+ # get curtrent cipher list - comma delimited
+ for cipher in config.return_values(path_ciphers):
+ tmp.extend(cipher.split(','))
+ # delete old cipher suite representation
+ config.delete(path_ciphers)
- config.set(base, value=tmp)
+ for cipher in tmp:
+ config.set(path_ciphers, value=cipher, replace=False)
- 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)
+# T4273: migrate ssh key-exchange list to multi node
+path_kex = base + ['key-exchange']
+if config.exists(path_kex):
+ tmp = []
+ # get curtrent cipher list - comma delimited
+ for kex in config.return_values(path_kex):
+ tmp.extend(kex.split(','))
+ # delete old cipher suite representation
+ config.delete(path_kex)
+
+ for kex in tmp:
+ config.set(path_kex, value=kex, replace=False)
+
+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/system/22-to-23 b/src/migration-scripts/system/22-to-23
new file mode 100755
index 000000000..7f832e48a
--- /dev/null
+++ b/src/migration-scripts/system/22-to-23
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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, 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', 'ipv6']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# T4346: drop support to disbale IPv6 address family within the OS Kernel
+if config.exists(base + ['disable']):
+ config.delete(base + ['disable'])
+ # IPv6 address family disable was the only CLI option set - we can cleanup
+ # the entire tree
+ if len(config.list_nodes(base)) == 0:
+ config.delete(base)
+
+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/op_mode/cpu_summary.py b/src/op_mode/cpu_summary.py
index cfd321522..3bdf5a718 100755
--- a/src/op_mode/cpu_summary.py
+++ b/src/op_mode/cpu_summary.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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,18 +19,30 @@ from vyos.util import colon_separated_to_dict
FILE_NAME = '/proc/cpuinfo'
-with open(FILE_NAME, 'r') as f:
- data_raw = f.read()
+def get_raw_data():
+ with open(FILE_NAME, 'r') as f:
+ data_raw = f.read()
-data = colon_separated_to_dict(data_raw)
+ data = colon_separated_to_dict(data_raw)
-# Accumulate all data in a dict for future support for machine-readable output
-cpu_data = {}
-cpu_data['cpu_number'] = len(data['processor'])
-cpu_data['models'] = list(set(data['model name']))
+ # Accumulate all data in a dict for future support for machine-readable output
+ cpu_data = {}
+ cpu_data['cpu_number'] = len(data['processor'])
+ cpu_data['models'] = list(set(data['model name']))
-# Strip extra whitespace from CPU model names, /proc/cpuinfo is prone to that
-cpu_data['models'] = map(lambda s: re.sub(r'\s+', ' ', s), cpu_data['models'])
+ # Strip extra whitespace from CPU model names, /proc/cpuinfo is prone to that
+ cpu_data['models'] = list(map(lambda s: re.sub(r'\s+', ' ', s), cpu_data['models']))
+
+ return cpu_data
+
+def get_formatted_output():
+ cpu_data = get_raw_data()
+
+ out = "CPU(s): {0}\n".format(cpu_data['cpu_number'])
+ out += "CPU model(s): {0}".format(",".join(cpu_data['models']))
+
+ return out
+
+if __name__ == '__main__':
+ print(get_formatted_output())
-print("CPU(s): {0}".format(cpu_data['cpu_number']))
-print("CPU model(s): {0}".format(",".join(cpu_data['models'])))
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index cf70890a6..3146fc357 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
+import ipaddress
import json
import re
import tabulate
@@ -87,7 +88,8 @@ def get_config_firewall(conf, name=None, ipv6=False, interfaces=True):
def get_nftables_details(name, ipv6=False):
suffix = '6' if ipv6 else ''
- command = f'sudo nft list chain ip{suffix} filter {name}'
+ name_prefix = 'NAME6_' if ipv6 else 'NAME_'
+ command = f'sudo nft list chain ip{suffix} filter {name_prefix}{name}'
try:
results = cmd(command)
except:
@@ -266,13 +268,17 @@ def show_firewall_group(name=None):
continue
references = find_references(group_type, group_name)
- row = [group_name, group_type, ', '.join(references)]
+ row = [group_name, group_type, '\n'.join(references) or 'N/A']
if 'address' in group_conf:
- row.append(", ".join(group_conf['address']))
+ row.append("\n".join(sorted(group_conf['address'], key=ipaddress.ip_address)))
elif 'network' in group_conf:
- row.append(", ".join(group_conf['network']))
+ row.append("\n".join(sorted(group_conf['network'], key=ipaddress.ip_network)))
+ elif 'mac_address' in group_conf:
+ row.append("\n".join(sorted(group_conf['mac_address'])))
elif 'port' in group_conf:
- row.append(", ".join(group_conf['port']))
+ row.append("\n".join(sorted(group_conf['port'])))
+ else:
+ row.append('N/A')
rows.append(row)
if rows:
@@ -302,7 +308,7 @@ def show_summary():
for name, name_conf in firewall['ipv6_name'].items():
description = name_conf.get('description', '')
interfaces = ", ".join(name_conf['interface'])
- v6_out.append([name, description, interfaces])
+ v6_out.append([name, description, interfaces or 'N/A'])
if v6_out:
print('\nIPv6 name:\n')
diff --git a/src/op_mode/generate_ovpn_client_file.py b/src/op_mode/generate_ovpn_client_file.py
new file mode 100755
index 000000000..29db41e37
--- /dev/null
+++ b/src/op_mode/generate_ovpn_client_file.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 argparse
+import os
+
+from jinja2 import Template
+
+from vyos.configquery import ConfigTreeQuery
+from vyos.ifconfig import Section
+from vyos.util import cmd
+
+
+client_config = """
+
+client
+nobind
+remote {{ remote_host }} {{ port }}
+remote-cert-tls server
+proto {{ 'tcp-client' if protocol == 'tcp-active' else 'udp' }}
+dev {{ device }}
+dev-type {{ device }}
+persist-key
+persist-tun
+verb 3
+
+# Encryption options
+{% if encryption is defined and encryption is not none %}
+{% if encryption.cipher is defined and encryption.cipher is not none %}
+cipher {{ encryption.cipher }}
+{% if encryption.cipher == 'bf128' %}
+keysize 128
+{% elif encryption.cipher == 'bf256' %}
+keysize 256
+{% endif %}
+{% endif %}
+{% if encryption.ncp_ciphers is defined and encryption.ncp_ciphers is not none %}
+data-ciphers {{ encryption.ncp_ciphers }}
+{% endif %}
+{% endif %}
+
+{% if hash is defined and hash is not none %}
+auth {{ hash }}
+{% endif %}
+keysize 256
+comp-lzo {{ '' if use_lzo_compression is defined else 'no' }}
+
+<ca>
+-----BEGIN CERTIFICATE-----
+{{ ca }}
+-----END CERTIFICATE-----
+
+</ca>
+
+<cert>
+-----BEGIN CERTIFICATE-----
+{{ cert }}
+-----END CERTIFICATE-----
+
+</cert>
+
+<key>
+-----BEGIN PRIVATE KEY-----
+{{ key }}
+-----END PRIVATE KEY-----
+
+</key>
+
+"""
+
+config = ConfigTreeQuery()
+base = ['interfaces', 'openvpn']
+
+if not config.exists(base):
+ print('OpenVPN not configured')
+ exit(0)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--interface", type=str, help='OpenVPN interface the client is connecting to', required=True)
+ parser.add_argument("-a", "--ca", type=str, help='OpenVPN CA cerificate', required=True)
+ parser.add_argument("-c", "--cert", type=str, help='OpenVPN client cerificate', required=True)
+ parser.add_argument("-k", "--key", type=str, help='OpenVPN client cerificate key', action="store")
+ args = parser.parse_args()
+
+ interface = args.interface
+ ca = args.ca
+ cert = args.cert
+ key = args.key
+ if not key:
+ key = args.cert
+
+ if interface not in Section.interfaces('openvpn'):
+ exit(f'OpenVPN interface "{interface}" does not exist!')
+
+ if not config.exists(['pki', 'ca', ca, 'certificate']):
+ exit(f'OpenVPN CA certificate "{ca}" does not exist!')
+
+ if not config.exists(['pki', 'certificate', cert, 'certificate']):
+ exit(f'OpenVPN certificate "{cert}" does not exist!')
+
+ if not config.exists(['pki', 'certificate', cert, 'private', 'key']):
+ exit(f'OpenVPN certificate key "{key}" does not exist!')
+
+ ca = config.value(['pki', 'ca', ca, 'certificate'])
+ cert = config.value(['pki', 'certificate', cert, 'certificate'])
+ key = config.value(['pki', 'certificate', key, 'private', 'key'])
+ remote_host = config.value(base + [interface, 'local-host'])
+
+ ovpn_conf = config.get_config_dict(base + [interface], key_mangling=('-', '_'), get_first_key=True)
+
+ port = '1194' if 'local_port' not in ovpn_conf else ovpn_conf['local_port']
+ proto = 'udp' if 'protocol' not in ovpn_conf else ovpn_conf['protocol']
+ device = 'tun' if 'device_type' not in ovpn_conf else ovpn_conf['device_type']
+
+ config = {
+ 'interface' : interface,
+ 'ca' : ca,
+ 'cert' : cert,
+ 'key' : key,
+ 'device' : device,
+ 'port' : port,
+ 'proto' : proto,
+ 'remote_host' : remote_host,
+ 'address' : [],
+ }
+
+# Clear out terminal first
+print('\x1b[2J\x1b[H')
+client = Template(client_config, trim_blocks=True).render(config)
+print(client)
diff --git a/src/op_mode/generate_public_key_command.py b/src/op_mode/generate_public_key_command.py
index 7a7b6c923..f071ae350 100755
--- a/src/op_mode/generate_public_key_command.py
+++ b/src/op_mode/generate_public_key_command.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2022 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,8 +29,12 @@ def get_key(path):
key_string = vyos.remote.get_remote_config(path)
return key_string.split()
-username = sys.argv[1]
-algorithm, key, identifier = get_key(sys.argv[2])
+try:
+ username = sys.argv[1]
+ algorithm, key, identifier = get_key(sys.argv[2])
+except Exception as e:
+ print("Failed to retrieve the public key: {}".format(e))
+ sys.exit(1)
print('# To add this key as an embedded key, run the following commands:')
print('configure')
@@ -39,3 +43,4 @@ print(f'set system login user {username} authentication public-keys {identifier}
print('commit')
print('save')
print('exit')
+
diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py
index b9ebc991a..17f6bf552 100755
--- a/src/op_mode/lldp_op.py
+++ b/src/op_mode/lldp_op.py
@@ -54,6 +54,7 @@ def parse_data(data, interface):
for local_if, values in neighbor.items():
if interface is not None and local_if != interface:
continue
+ cap = ''
for chassis, c_value in values.get('chassis', {}).items():
# bail out early if no capabilities found
if 'capability' not in c_value:
@@ -62,7 +63,6 @@ def parse_data(data, interface):
if isinstance(capabilities, dict):
capabilities = [capabilities]
- cap = ''
for capability in capabilities:
if capability['enabled']:
if capability['type'] == 'Router':
diff --git a/src/op_mode/monitor_bandwidth_test.sh b/src/op_mode/monitor_bandwidth_test.sh
index 900223bca..a6ad0b42c 100755
--- a/src/op_mode/monitor_bandwidth_test.sh
+++ b/src/op_mode/monitor_bandwidth_test.sh
@@ -24,6 +24,9 @@ elif [[ $(dig $1 AAAA +short | grep -v '\.$' | wc -l) -gt 0 ]]; then
# Set address family to IPv6 when FQDN has at least one AAAA record
OPT="-V"
+else
+ # It's not IPv6, no option needed
+ OPT=""
fi
/usr/bin/iperf $OPT -c $1 $2
diff --git a/src/op_mode/policy_route.py b/src/op_mode/policy_route.py
index e0b4ac514..5be40082f 100755
--- a/src/op_mode/policy_route.py
+++ b/src/op_mode/policy_route.py
@@ -26,7 +26,7 @@ def get_policy_interfaces(conf, policy, name=None, ipv6=False):
interfaces = conf.get_config_dict(['interfaces'], key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
- routes = ['route', 'ipv6_route']
+ routes = ['route', 'route6']
def parse_if(ifname, if_conf):
if 'policy' in if_conf:
@@ -52,7 +52,7 @@ def get_policy_interfaces(conf, policy, name=None, ipv6=False):
def get_config_policy(conf, name=None, ipv6=False, interfaces=True):
config_path = ['policy']
if name:
- config_path += ['ipv6-route' if ipv6 else 'route', name]
+ config_path += ['route6' if ipv6 else 'route', name]
policy = conf.get_config_dict(config_path, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
@@ -64,8 +64,8 @@ def get_config_policy(conf, name=None, ipv6=False, interfaces=True):
for route_name, route_conf in policy['route'].items():
route_conf['interface'] = []
- if 'ipv6_route' in policy:
- for route_name, route_conf in policy['ipv6_route'].items():
+ if 'route6' in policy:
+ for route_name, route_conf in policy['route6'].items():
route_conf['interface'] = []
get_policy_interfaces(conf, policy, name, ipv6)
@@ -151,8 +151,8 @@ def show_policy(ipv6=False):
for route, route_conf in policy['route'].items():
output_policy_route(route, route_conf, ipv6=False)
- if ipv6 and 'ipv6_route' in policy:
- for route, route_conf in policy['ipv6_route'].items():
+ if ipv6 and 'route6' in policy:
+ for route, route_conf in policy['route6'].items():
output_policy_route(route, route_conf, ipv6=True)
def show_policy_name(name, ipv6=False):
diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py
index 679b03c0b..fd4f86d88 100755
--- a/src/op_mode/powerctrl.py
+++ b/src/op_mode/powerctrl.py
@@ -33,10 +33,12 @@ def utc2local(datetime):
def parse_time(s):
try:
- if re.match(r'^\d{1,2}$', s):
- if (int(s) > 59):
+ if re.match(r'^\d{1,9999}$', s):
+ if (int(s) > 59) and (int(s) < 1440):
s = str(int(s)//60) + ":" + str(int(s)%60)
return datetime.strptime(s, "%H:%M").time()
+ if (int(s) >= 1440):
+ return s.split()
else:
return datetime.strptime(s, "%M").time()
else:
@@ -141,7 +143,7 @@ def execute_shutdown(time, reboot=True, ask=True):
cmd(f'/usr/bin/wall "{wall_msg}"')
else:
if not ts:
- exit(f'Invalid time "{time[0]}". The valid format is HH:MM')
+ exit(f'Invalid time "{time[0]}". Uses 24 Hour Clock format')
else:
exit(f'Invalid date "{time[1]}". A valid format is YYYY-MM-DD [HH:MM]')
else:
@@ -172,7 +174,12 @@ def main():
action.add_argument("--reboot", "-r",
help="Reboot the system",
nargs="*",
- metavar="Minutes|HH:MM")
+ metavar="HH:MM")
+
+ action.add_argument("--reboot_in", "-i",
+ help="Reboot the system",
+ nargs="*",
+ metavar="Minutes")
action.add_argument("--poweroff", "-p",
help="Poweroff the system",
@@ -190,7 +197,17 @@ def main():
try:
if args.reboot is not None:
+ for r in args.reboot:
+ if ':' not in r and '/' not in r and '.' not in r:
+ print("Incorrect format! Use HH:MM")
+ exit(1)
execute_shutdown(args.reboot, reboot=True, ask=args.yes)
+ if args.reboot_in is not None:
+ for i in args.reboot_in:
+ if ':' in i:
+ print("Incorrect format! Use Minutes")
+ exit(1)
+ execute_shutdown(args.reboot_in, reboot=True, ask=args.yes)
if args.poweroff is not None:
execute_shutdown(args.poweroff, reboot=False, ask=args.yes)
if args.cancel:
diff --git a/src/op_mode/show_cpu.py b/src/op_mode/show_cpu.py
index 0040e950d..9973d9789 100755
--- a/src/op_mode/show_cpu.py
+++ b/src/op_mode/show_cpu.py
@@ -21,7 +21,7 @@ from sys import exit
from vyos.util import popen, DEVNULL
OUT_TMPL_SRC = """
-{% if cpu %}
+{%- if cpu -%}
{% if 'vendor' in cpu %}CPU Vendor: {{cpu.vendor}}{% endif %}
{% if 'model' in cpu %}Model: {{cpu.model}}{% endif %}
{% if 'cpus' in cpu %}Total CPUs: {{cpu.cpus}}{% endif %}
@@ -31,31 +31,42 @@ OUT_TMPL_SRC = """
{% if 'mhz' in cpu %}Current MHz: {{cpu.mhz}}{% endif %}
{% if 'mhz_min' in cpu %}Minimum MHz: {{cpu.mhz_min}}{% endif %}
{% if 'mhz_max' in cpu %}Maximum MHz: {{cpu.mhz_max}}{% endif %}
-{% endif %}
+{%- endif -%}
"""
-cpu = {}
-cpu_json, code = popen('lscpu -J', stderr=DEVNULL)
-
-if code == 0:
- cpu_info = json.loads(cpu_json)
- if len(cpu_info) > 0 and 'lscpu' in cpu_info:
- for prop in cpu_info['lscpu']:
- if (prop['field'].find('Thread(s)') > -1): cpu['threads'] = prop['data']
- if (prop['field'].find('Core(s)')) > -1: cpu['cores'] = prop['data']
- if (prop['field'].find('Socket(s)')) > -1: cpu['sockets'] = prop['data']
- if (prop['field'].find('CPU(s):')) > -1: cpu['cpus'] = prop['data']
- if (prop['field'].find('CPU MHz')) > -1: cpu['mhz'] = prop['data']
- if (prop['field'].find('CPU min MHz')) > -1: cpu['mhz_min'] = prop['data']
- if (prop['field'].find('CPU max MHz')) > -1: cpu['mhz_max'] = prop['data']
- if (prop['field'].find('Vendor ID')) > -1: cpu['vendor'] = prop['data']
- if (prop['field'].find('Model name')) > -1: cpu['model'] = prop['data']
-
-if len(cpu) > 0:
- tmp = { 'cpu':cpu }
+def get_raw_data():
+ cpu = {}
+ cpu_json, code = popen('lscpu -J', stderr=DEVNULL)
+
+ if code == 0:
+ cpu_info = json.loads(cpu_json)
+ if len(cpu_info) > 0 and 'lscpu' in cpu_info:
+ for prop in cpu_info['lscpu']:
+ if (prop['field'].find('Thread(s)') > -1): cpu['threads'] = prop['data']
+ if (prop['field'].find('Core(s)')) > -1: cpu['cores'] = prop['data']
+ if (prop['field'].find('Socket(s)')) > -1: cpu['sockets'] = prop['data']
+ if (prop['field'].find('CPU(s):')) > -1: cpu['cpus'] = prop['data']
+ if (prop['field'].find('CPU MHz')) > -1: cpu['mhz'] = prop['data']
+ if (prop['field'].find('CPU min MHz')) > -1: cpu['mhz_min'] = prop['data']
+ if (prop['field'].find('CPU max MHz')) > -1: cpu['mhz_max'] = prop['data']
+ if (prop['field'].find('Vendor ID')) > -1: cpu['vendor'] = prop['data']
+ if (prop['field'].find('Model name')) > -1: cpu['model'] = prop['data']
+
+ return cpu
+
+def get_formatted_output():
+ cpu = get_raw_data()
+
+ tmp = {'cpu':cpu}
tmpl = Template(OUT_TMPL_SRC)
- print(tmpl.render(tmp))
- exit(0)
-else:
- print('CPU information could not be determined\n')
- exit(1)
+ return tmpl.render(tmp)
+
+if __name__ == '__main__':
+ cpu = get_raw_data()
+
+ if len(cpu) > 0:
+ print(get_formatted_output())
+ else:
+ print('CPU information could not be determined\n')
+ exit(1)
+
diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py
index e72f0f965..5b8f00dba 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) 2022 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,119 +14,117 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import re
-import sys
+from re import split as re_split
+from sys import exit
-import vici
-import tabulate
-import hurry.filesize
+from hurry import filesize
+from tabulate import tabulate
+from vici import Session as vici_session
+
+from vyos.util import seconds_to_human
-import vyos.util
def convert(text):
return int(text) if text.isdigit() else text.lower()
+
def alphanum_key(key):
- return [convert(c) for c in re.split('([0-9]+)', str(key))]
+ return [convert(c) for c in re_split('([0-9]+)', str(key))]
-def format_output(conns, sas):
+
+def format_output(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"}
-
- # parent_sa["state"] = IKE state, child_sas["state"] = ESP state
- state = 'down'
- uptime = 'N/A'
-
- if parent_sa["state"] == b"ESTABLISHED" and installed_sas:
- state = "up"
-
- remote_host = parent_sa["remote-host"].decode()
- remote_id = parent_sa["remote-id"].decode()
-
- if remote_host == remote_id:
- remote_id = "N/A"
-
- # The counters can only be obtained from the child SAs
- 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)
- 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)
+ for sa in sas:
+ for parent_sa in sa.values():
+ # create an item for each child-sa
+ for child_sa in parent_sa.get('child-sas', {}).values():
+ # prepare a list for output data
+ sa_out_name = sa_out_state = sa_out_uptime = sa_out_bytes = sa_out_packets = sa_out_remote_addr = sa_out_remote_id = sa_out_proposal = 'N/A'
+
+ # collect raw data
+ sa_name = child_sa.get('name')
+ sa_state = child_sa.get('state')
+ sa_uptime = child_sa.get('install-time')
+ sa_bytes_in = child_sa.get('bytes-in')
+ sa_bytes_out = child_sa.get('bytes-out')
+ sa_packets_in = child_sa.get('packets-in')
+ sa_packets_out = child_sa.get('packets-out')
+ sa_remote_addr = parent_sa.get('remote-host')
+ sa_remote_id = parent_sa.get('remote-id')
+ sa_proposal_encr_alg = child_sa.get('encr-alg')
+ sa_proposal_integ_alg = child_sa.get('integ-alg')
+ sa_proposal_encr_keysize = child_sa.get('encr-keysize')
+ sa_proposal_dh_group = child_sa.get('dh-group')
+
+ # format data to display
+ if sa_name:
+ sa_out_name = sa_name.decode()
+ if sa_state:
+ if sa_state == b'INSTALLED':
+ sa_out_state = 'up'
+ else:
+ sa_out_state = 'down'
+ if sa_uptime:
+ sa_out_uptime = seconds_to_human(sa_uptime.decode())
+ if sa_bytes_in and sa_bytes_out:
+ bytes_in = filesize.size(int(sa_bytes_in.decode()))
+ bytes_out = filesize.size(int(sa_bytes_out.decode()))
+ sa_out_bytes = f'{bytes_in}/{bytes_out}'
+ if sa_packets_in and sa_packets_out:
+ packets_in = filesize.size(int(sa_packets_in.decode()),
+ system=filesize.si)
+ packets_out = filesize.size(int(sa_packets_out.decode()),
+ system=filesize.si)
+ sa_out_packets = f'{packets_in}/{packets_out}'
+ if sa_remote_addr:
+ sa_out_remote_addr = sa_remote_addr.decode()
+ if sa_remote_id:
+ sa_out_remote_id = sa_remote_id.decode()
+ # format proposal
+ if sa_proposal_encr_alg:
+ sa_out_proposal = sa_proposal_encr_alg.decode()
+ if sa_proposal_encr_keysize:
+ sa_proposal_encr_keysize_str = sa_proposal_encr_keysize.decode()
+ sa_out_proposal = f'{sa_out_proposal}_{sa_proposal_encr_keysize_str}'
+ if sa_proposal_integ_alg:
+ sa_proposal_integ_alg_str = sa_proposal_integ_alg.decode()
+ sa_out_proposal = f'{sa_out_proposal}/{sa_proposal_integ_alg_str}'
+ if sa_proposal_dh_group:
+ sa_proposal_dh_group_str = sa_proposal_dh_group.decode()
+ sa_out_proposal = f'{sa_out_proposal}/{sa_proposal_dh_group_str}'
+
+ # add a new item to output data
+ sa_data.append([
+ sa_out_name, sa_out_state, sa_out_uptime, sa_out_bytes,
+ sa_out_packets, sa_out_remote_addr, sa_out_remote_id,
+ sa_out_proposal
+ ])
+
+ # return output data
return sa_data
+
if __name__ == '__main__':
try:
- session = vici.Session()
- conns = {}
- sas = {}
+ session = vici_session()
+ sas = list(session.list_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 = format_output(sas)
sa_data = sorted(sa_data, key=alphanum_key)
- output = tabulate.tabulate(sa_data, headers)
+
+ headers = [
+ "Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out",
+ "Remote address", "Remote ID", "Proposal"
+ ]
+ output = tabulate(sa_data, headers)
print(output)
except PermissionError:
print("You do not have a permission to connect to the IPsec daemon")
- sys.exit(1)
+ exit(1)
except ConnectionRefusedError:
print("IPsec is not runing")
- sys.exit(1)
+ exit(1)
except Exception as e:
print("An error occured: {0}".format(e))
- sys.exit(1)
+ exit(1)
diff --git a/src/op_mode/show_ram.py b/src/op_mode/show_ram.py
index 5818ec132..2b0be3965 100755
--- a/src/op_mode/show_ram.py
+++ b/src/op_mode/show_ram.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2022 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
@@ -55,10 +55,17 @@ def get_system_memory_human():
return mem
-if __name__ == '__main__':
- mem = get_system_memory_human()
+def get_raw_data():
+ return get_system_memory_human()
+
+def get_formatted_output():
+ mem = get_raw_data()
- print("Total: {}".format(mem["total"]))
- print("Free: {}".format(mem["free"]))
- print("Used: {}".format(mem["used"]))
+ out = "Total: {}\n".format(mem["total"])
+ out += "Free: {}\n".format(mem["free"])
+ out += "Used: {}".format(mem["used"])
+ return out
+
+if __name__ == '__main__':
+ print(get_formatted_output())
diff --git a/src/op_mode/show_uptime.py b/src/op_mode/show_uptime.py
index c3dea52e6..1b5e33fa9 100755
--- a/src/op_mode/show_uptime.py
+++ b/src/op_mode/show_uptime.py
@@ -37,14 +37,27 @@ def get_load_averages():
return res
-if __name__ == '__main__':
+def get_raw_data():
from vyos.util import seconds_to_human
- print("Uptime: {}\n".format(seconds_to_human(get_uptime_seconds())))
+ res = {}
+ res["uptime_seconds"] = get_uptime_seconds()
+ res["uptime"] = seconds_to_human(get_uptime_seconds())
+ res["load_average"] = get_load_averages()
+
+ return res
- avgs = get_load_averages()
+def get_formatted_output():
+ data = get_raw_data()
- print("Load averages:")
- print("1 minute: {:.02f}%".format(avgs[1]*100))
- print("5 minutes: {:.02f}%".format(avgs[5]*100))
- print("15 minutes: {:.02f}%".format(avgs[15]*100))
+ out = "Uptime: {}\n\n".format(data["uptime"])
+ avgs = data["load_average"]
+ out += "Load averages:\n"
+ out += "1 minute: {:.02f}%\n".format(avgs[1]*100)
+ out += "5 minutes: {:.02f}%\n".format(avgs[5]*100)
+ out += "15 minutes: {:.02f}%\n".format(avgs[15]*100)
+
+ return out
+
+if __name__ == '__main__':
+ print(get_formatted_output())
diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py
index 7962e1e7b..b82ab6eca 100755
--- a/src/op_mode/show_version.py
+++ b/src/op_mode/show_version.py
@@ -26,10 +26,6 @@ from jinja2 import Template
from sys import exit
from vyos.util import call
-parser = argparse.ArgumentParser()
-parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output")
-parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output")
-
version_output_tmpl = """
Version: VyOS {{version}}
Release train: {{release_train}}
@@ -51,7 +47,20 @@ Hardware UUID: {{hardware_uuid}}
Copyright: VyOS maintainers and contributors
"""
+def get_raw_data():
+ version_data = vyos.version.get_full_version_data()
+ return version_data
+
+def get_formatted_output():
+ version_data = get_raw_data()
+ tmpl = Template(version_output_tmpl)
+ return tmpl.render(version_data)
+
if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output")
+ parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output")
+
args = parser.parse_args()
version_data = vyos.version.get_full_version_data()
@@ -60,9 +69,8 @@ if __name__ == '__main__':
import json
print(json.dumps(version_data))
exit(0)
-
- tmpl = Template(version_output_tmpl)
- print(tmpl.render(version_data))
+ else:
+ print(get_formatted_output())
if args.funny:
print(vyos.limericks.get_random())
diff --git a/src/op_mode/show_virtual_server.py b/src/op_mode/show_virtual_server.py
new file mode 100755
index 000000000..377180dec
--- /dev/null
+++ b/src/op_mode/show_virtual_server.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 ConfigTreeQuery
+from vyos.util import call
+
+def is_configured():
+ """ Check if high-availability virtual-server is configured """
+ config = ConfigTreeQuery()
+ if not config.exists(['high-availability', 'virtual-server']):
+ return False
+ return True
+
+if __name__ == '__main__':
+
+ if is_configured() == False:
+ print('Virtual server not configured!')
+ exit(0)
+
+ call('sudo ipvsadm --list --numeric')
diff --git a/src/op_mode/vrrp.py b/src/op_mode/vrrp.py
index 2c1db20bf..dab146d28 100755
--- a/src/op_mode/vrrp.py
+++ b/src/op_mode/vrrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -23,6 +23,7 @@ import tabulate
import vyos.util
+from vyos.configquery import ConfigTreeQuery
from vyos.ifconfig.vrrp import VRRP
from vyos.ifconfig.vrrp import VRRPError, VRRPNoData
@@ -35,7 +36,17 @@ group.add_argument("-d", "--data", action="store_true", help="Print detailed VRR
args = parser.parse_args()
+def is_configured():
+ """ Check if VRRP is configured """
+ config = ConfigTreeQuery()
+ if not config.exists(['high-availability', 'vrrp', 'group']):
+ return False
+ return True
+
# Exit early if VRRP is dead or not configured
+if is_configured() == False:
+ print('VRRP not configured!')
+ exit(0)
if not VRRP.is_running():
print('VRRP is not running')
sys.exit(0)
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 06871f1d6..c1b595412 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -352,7 +352,7 @@ class MultipartRoute(APIRoute):
return error(e.status_code, e.detail)
except Exception as e:
if request.ERR_MISSING_KEY:
- return error(422, "Valid API key is required")
+ return error(401, "Valid API key is required")
if request.ERR_MISSING_DATA:
return error(422, "Non-empty data field is required")
if request.ERR_NOT_JSON:
@@ -648,10 +648,12 @@ if __name__ == '__main__':
app.state.vyos_keys = server_config['api_keys']
app.state.vyos_debug = server_config['debug']
+ app.state.vyos_gql = server_config['gql']
app.state.vyos_strict = server_config['strict']
app.state.vyos_origins = server_config.get('cors', {}).get('origins', [])
- graphql_init(app)
+ if app.state.vyos_gql:
+ graphql_init(app)
try:
if not server_config['socket']:
diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py
index b1fe7e43f..a8df232ae 100755
--- a/src/system/keepalived-fifo.py
+++ b/src/system/keepalived-fifo.py
@@ -71,7 +71,8 @@ class KeepalivedFifo:
# Read VRRP configuration directly from CLI
self.vrrp_config_dict = conf.get_config_dict(base,
- key_mangling=('-', '_'), get_first_key=True)
+ key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
logger.debug(f'Loaded configuration: {self.vrrp_config_dict}')
except Exception as err:
diff --git a/src/systemd/miniupnpd.service b/src/systemd/miniupnpd.service
new file mode 100644
index 000000000..51cb2eed8
--- /dev/null
+++ b/src/systemd/miniupnpd.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=UPnP service
+ConditionPathExists=/run/upnp/miniupnp.conf
+After=vyos-router.service
+StartLimitIntervalSec=0
+
+[Service]
+WorkingDirectory=/run/upnp
+Type=simple
+ExecStart=/usr/sbin/miniupnpd -d -f /run/upnp/miniupnp.conf
+PrivateTmp=yes
+PIDFile=/run/miniupnpd.pid
+Restart=on-failure
diff --git a/src/tests/test_util.py b/src/tests/test_util.py
index 9bd27adc0..8ac9a500a 100644
--- a/src/tests/test_util.py
+++ b/src/tests/test_util.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -23,3 +23,6 @@ class TestVyOSUtil(TestCase):
expected_data = {"foo_bar": {"baz_quux": None}}
new_data = mangle_dict_keys(data, '-', '_')
self.assertEqual(new_data, expected_data)
+
+ def test_sysctl_read(self):
+ self.assertEqual(sysctl_read('net.ipv4.conf.lo.forwarding'), '1')
diff --git a/src/validators/ip-address b/src/validators/ip-address
index 51fb72c85..11d6df09e 100755
--- a/src/validators/ip-address
+++ b/src/validators/ip-address
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-any-single $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IP address"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ip-cidr b/src/validators/ip-cidr
index 987bf84ca..60d2ac295 100755
--- a/src/validators/ip-cidr
+++ b/src/validators/ip-cidr
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-any-cidr $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IP CIDR"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ip-host b/src/validators/ip-host
index f2906e8cf..77c578fa2 100755
--- a/src/validators/ip-host
+++ b/src/validators/ip-host
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-any-host $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IP host"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ip-prefix b/src/validators/ip-prefix
index e58aad395..e5a64fea8 100755
--- a/src/validators/ip-prefix
+++ b/src/validators/ip-prefix
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-any-net $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IP prefix"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ip-protocol b/src/validators/ip-protocol
index 7898fa6d0..c4c882502 100755
--- a/src/validators/ip-protocol
+++ b/src/validators/ip-protocol
@@ -38,4 +38,5 @@ if __name__ == '__main__':
if re.match(pattern, input):
exit(0)
+ print(f'Error: {input} is not a valid IP protocol')
exit(1)
diff --git a/src/validators/ipv4 b/src/validators/ipv4
index 53face090..8676d5800 100755
--- a/src/validators/ipv4
+++ b/src/validators/ipv4
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv4 $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not IPv4"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv4-address b/src/validators/ipv4-address
index 872a7645a..058db088b 100755
--- a/src/validators/ipv4-address
+++ b/src/validators/ipv4-address
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv4-single $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv4 address"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv4-host b/src/validators/ipv4-host
index f42feffa4..74b8c36a7 100755
--- a/src/validators/ipv4-host
+++ b/src/validators/ipv4-host
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv4-host $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv4 host"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv4-multicast b/src/validators/ipv4-multicast
index 5465c728d..3f28c51db 100755
--- a/src/validators/ipv4-multicast
+++ b/src/validators/ipv4-multicast
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv4-multicast $1 && ipaddrcheck --is-ipv4-single $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv4 multicast address"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv4-prefix b/src/validators/ipv4-prefix
index 8ec8a2c45..7e1e0e8dd 100755
--- a/src/validators/ipv4-prefix
+++ b/src/validators/ipv4-prefix
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv4-net $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv4 prefix"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv4-range b/src/validators/ipv4-range
index cc59039f1..6492bfc52 100755
--- a/src/validators/ipv4-range
+++ b/src/validators/ipv4-range
@@ -7,6 +7,11 @@ ip2dec () {
printf '%d\n' "$((a * 256 ** 3 + b * 256 ** 2 + c * 256 + d))"
}
+error_exit() {
+ echo "Error: $1 is not a valid IPv4 address range"
+ exit 1
+}
+
# Only run this if there is a hypen present in $1
if [[ "$1" =~ "-" ]]; then
# This only works with real bash (<<<) - split IP addresses into array with
@@ -15,21 +20,21 @@ if [[ "$1" =~ "-" ]]; then
ipaddrcheck --is-ipv4-single ${strarr[0]}
if [ $? -gt 0 ]; then
- exit 1
+ error_exit $1
fi
ipaddrcheck --is-ipv4-single ${strarr[1]}
if [ $? -gt 0 ]; then
- exit 1
+ error_exit $1
fi
start=$(ip2dec ${strarr[0]})
stop=$(ip2dec ${strarr[1]})
if [ $start -ge $stop ]; then
- exit 1
+ error_exit $1
fi
exit 0
fi
-exit 1
+error_exit $1
diff --git a/src/validators/ipv6 b/src/validators/ipv6
index f18d4a63e..4ae130eb5 100755
--- a/src/validators/ipv6
+++ b/src/validators/ipv6
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv6 $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not IPv6"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv6-address b/src/validators/ipv6-address
index e5d68d756..1fca77668 100755
--- a/src/validators/ipv6-address
+++ b/src/validators/ipv6-address
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv6-single $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv6 address"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv6-host b/src/validators/ipv6-host
index f7a745077..7085809a9 100755
--- a/src/validators/ipv6-host
+++ b/src/validators/ipv6-host
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv6-host $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv6 host"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv6-multicast b/src/validators/ipv6-multicast
index 5afc437e5..5aa7d734a 100755
--- a/src/validators/ipv6-multicast
+++ b/src/validators/ipv6-multicast
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv6-multicast $1 && ipaddrcheck --is-ipv6-single $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv6 multicast address"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv6-prefix b/src/validators/ipv6-prefix
index e43616350..890dda723 100755
--- a/src/validators/ipv6-prefix
+++ b/src/validators/ipv6-prefix
@@ -1,3 +1,10 @@
#!/bin/sh
ipaddrcheck --is-ipv6-net $1
+
+if [ $? -gt 0 ]; then
+ echo "Error: $1 is not a valid IPv6 prefix"
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/src/validators/ipv6-range b/src/validators/ipv6-range
index 033b6461b..7080860c4 100755
--- a/src/validators/ipv6-range
+++ b/src/validators/ipv6-range
@@ -1,16 +1,20 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
-import sys
-import re
-from vyos.template import is_ipv6
+from ipaddress import IPv6Address
+from sys import argv, exit
if __name__ == '__main__':
- if len(sys.argv)>1:
- ipv6_range = sys.argv[1]
- # Regex for ipv6-ipv6 https://regexr.com/
- if re.search('([a-f0-9:]+:+)+[a-f0-9]+-([a-f0-9:]+:+)+[a-f0-9]+', ipv6_range):
- for tmp in ipv6_range.split('-'):
- if not is_ipv6(tmp):
- sys.exit(1)
-
- sys.exit(0)
+ if len(argv) > 1:
+ # try to pass validation and raise an error if failed
+ try:
+ ipv6_range = argv[1]
+ range_left = ipv6_range.split('-')[0]
+ range_right = ipv6_range.split('-')[1]
+ if not IPv6Address(range_left) < IPv6Address(range_right):
+ raise ValueError(f'left element {range_left} must be less than right element {range_right}')
+ except Exception as err:
+ print(f'Error: {ipv6_range} is not a valid IPv6 range: {err}')
+ exit(1)
+ else:
+ print('Error: an IPv6 range argument must be provided')
+ exit(1)
diff --git a/src/validators/mac-address-firewall b/src/validators/mac-address-firewall
new file mode 100755
index 000000000..70551f86d
--- /dev/null
+++ b/src/validators/mac-address-firewall
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import sys
+
+pattern = "^!?([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$"
+
+if __name__ == '__main__':
+ if len(sys.argv) != 2:
+ sys.exit(1)
+ if not re.match(pattern, sys.argv[1]):
+ sys.exit(1)
+ sys.exit(0)
diff --git a/src/validators/port-multi b/src/validators/port-multi
new file mode 100755
index 000000000..cef371563
--- /dev/null
+++ b/src/validators/port-multi
@@ -0,0 +1,45 @@
+#!/usr/bin/python3
+
+import sys
+import re
+
+from vyos.util import read_file
+
+services_file = '/etc/services'
+
+def get_services():
+ names = []
+ service_data = read_file(services_file, "")
+ for line in service_data.split("\n"):
+ if not line or line[0] == '#':
+ continue
+ names.append(line.split(None, 1)[0])
+ return names
+
+if __name__ == '__main__':
+ if len(sys.argv)>1:
+ ports = sys.argv[1].split(",")
+ services = get_services()
+
+ for port in ports:
+ if port and port[0] == '!':
+ port = port[1:]
+ if re.match('^[0-9]{1,5}-[0-9]{1,5}$', port):
+ port_1, port_2 = port.split('-')
+ if int(port_1) not in range(1, 65536) or int(port_2) not in range(1, 65536):
+ print(f'Error: {port} is not a valid port range')
+ sys.exit(1)
+ if int(port_1) > int(port_2):
+ print(f'Error: {port} is not a valid port range')
+ sys.exit(1)
+ elif port.isnumeric():
+ if int(port) not in range(1, 65536):
+ print(f'Error: {port} is not a valid port')
+ sys.exit(1)
+ elif port not in services:
+ print(f'Error: {port} is not a valid service name')
+ sys.exit(1)
+ else:
+ sys.exit(2)
+
+ sys.exit(0)
diff --git a/src/validators/port-range b/src/validators/port-range
index abf0b09d5..5468000a7 100755
--- a/src/validators/port-range
+++ b/src/validators/port-range
@@ -3,16 +3,37 @@
import sys
import re
+from vyos.util import read_file
+
+services_file = '/etc/services'
+
+def get_services():
+ names = []
+ service_data = read_file(services_file, "")
+ for line in service_data.split("\n"):
+ if not line or line[0] == '#':
+ continue
+ names.append(line.split(None, 1)[0])
+ return names
+
+def error(port_range):
+ print(f'Error: {port_range} is not a valid port or port range')
+ sys.exit(1)
+
if __name__ == '__main__':
if len(sys.argv)>1:
port_range = sys.argv[1]
- if re.search('[0-9]{1,5}-[0-9]{1,5}', port_range):
- for tmp in port_range.split('-'):
- if int(tmp) not in range(1, 65535):
- sys.exit(1)
- else:
- if int(port_range) not in range(1, 65535):
- sys.exit(1)
+ if re.match('^[0-9]{1,5}-[0-9]{1,5}$', port_range):
+ port_1, port_2 = port_range.split('-')
+ if int(port_1) not in range(1, 65536) or int(port_2) not in range(1, 65536):
+ error(port_range)
+ if int(port_1) > int(port_2):
+ error(port_range)
+ elif port_range.isnumeric() and int(port_range) not in range(1, 65536):
+ error(port_range)
+ elif not port_range.isnumeric() and port_range not in get_services():
+ print(f'Error: {port_range} is not a valid service name')
+ sys.exit(1)
else:
sys.exit(2)
diff --git a/src/validators/tcp-flag b/src/validators/tcp-flag
new file mode 100755
index 000000000..1496b904a
--- /dev/null
+++ b/src/validators/tcp-flag
@@ -0,0 +1,17 @@
+#!/usr/bin/python3
+
+import sys
+import re
+
+if __name__ == '__main__':
+ if len(sys.argv)>1:
+ flag = sys.argv[1]
+ if flag and flag[0] == '!':
+ flag = flag[1:]
+ if flag not in ['syn', 'ack', 'rst', 'fin', 'urg', 'psh', 'ecn', 'cwr']:
+ print(f'Error: {flag} is not a valid TCP flag')
+ sys.exit(1)
+ else:
+ sys.exit(2)
+
+ sys.exit(0)
diff --git a/test-requirements.txt b/test-requirements.txt
index 9348520b5..a475e0a16 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,3 +3,4 @@ lxml
pylint
nose
coverage
+jinja2