summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/labeler.yml12
-rw-r--r--.github/workflows/pr-conflicts.yml2
-rw-r--r--.github/workflows/pull-request-labels.yml20
-rw-r--r--.gitignore5
-rw-r--r--.vscode/settings.json18
-rw-r--r--CONTRIBUTING.md14
-rw-r--r--Makefile11
-rw-r--r--README.md7
-rw-r--r--data/config-mode-dependencies.json3
-rw-r--r--data/configd-include.json3
-rw-r--r--data/op-mode-standardized.json1
-rw-r--r--data/templates/accel-ppp/ipoe.config.j24
-rw-r--r--data/templates/chrony/chrony.conf.j24
-rw-r--r--data/templates/dhcp-client/daemon-options.j24
-rw-r--r--data/templates/dhcp-client/ipv6.override.conf.j212
-rw-r--r--data/templates/dhcp-client/override.conf.j215
-rw-r--r--data/templates/dns-dynamic/ddclient.conf.j275
-rw-r--r--data/templates/dns-dynamic/override.conf.j210
-rw-r--r--data/templates/dynamic-dns/ddclient.conf.j253
-rw-r--r--data/templates/firewall/nftables-policy.j212
-rw-r--r--data/templates/firewall/nftables-zone.j278
-rw-r--r--data/templates/firewall/nftables.j2338
-rw-r--r--data/templates/frr/bgpd.frr.j216
-rw-r--r--data/templates/frr/daemons.frr.tmpl1
-rw-r--r--data/templates/frr/ospf6d.frr.j221
-rw-r--r--data/templates/frr/ospfd.frr.j232
-rw-r--r--data/templates/high-availability/keepalived.conf.j25
-rw-r--r--data/templates/ids/fastnetmon.j211
-rw-r--r--data/templates/ids/fastnetmon_networks_list.j22
-rw-r--r--data/templates/lldp/vyos.conf.j22
-rw-r--r--data/templates/login/nsswitch.conf.j221
-rw-r--r--data/templates/login/tacplus_nss.conf.j274
-rw-r--r--data/templates/login/tacplus_servers.j259
-rw-r--r--data/templates/mdns-repeater/avahi-daemon.j21
-rw-r--r--data/templates/openvpn/server.conf.j213
-rw-r--r--data/templates/pmacct/uacctd.conf.j24
-rw-r--r--data/templates/rsyslog/logrotate.j211
-rw-r--r--data/templates/rsyslog/rsyslog.conf.j227
-rw-r--r--data/templates/vpp/override.conf.j214
-rw-r--r--data/templates/vpp/startup.conf.j2116
-rw-r--r--data/templates/wifi/hostapd.conf.j226
-rw-r--r--data/templates/wifi/hostapd_accept_station.conf.j27
-rw-r--r--data/templates/wifi/hostapd_deny_station.conf.j27
-rw-r--r--data/templates/zabbix-agent/10-override.conf.j214
-rw-r--r--data/templates/zabbix-agent/zabbix-agent.conf.j277
-rw-r--r--data/vyos-firewall-init.conf12
-rw-r--r--debian/control22
-rwxr-xr-xdebian/rules20
-rw-r--r--debian/vyos-1x.install1
-rw-r--r--debian/vyos-1x.links1
-rw-r--r--debian/vyos-1x.postinst100
-rw-r--r--debian/vyos-1x.preinst4
-rw-r--r--interface-definitions/container.xml.in65
-rw-r--r--interface-definitions/dhcp-relay.xml.in1
-rw-r--r--interface-definitions/dhcp-server.xml.in2
-rw-r--r--interface-definitions/dhcpv6-relay.xml.in1
-rw-r--r--interface-definitions/dns-dynamic.xml.in208
-rw-r--r--interface-definitions/dns-forwarding.xml.in2
-rw-r--r--interface-definitions/firewall.xml.in699
-rw-r--r--interface-definitions/high-availability.xml.in19
-rw-r--r--interface-definitions/include/accel-ppp/client-ip-pool-subnet-single.xml.i3
-rw-r--r--interface-definitions/include/address-ipv4-ipv6-single.xml.i18
-rw-r--r--interface-definitions/include/bgp/afi-export-import.xml.i1
-rw-r--r--interface-definitions/include/bgp/afi-label.xml.i13
-rw-r--r--interface-definitions/include/bgp/afi-vpn-label.xml.i14
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i108
-rw-r--r--interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i4
-rw-r--r--interface-definitions/include/dns/dynamic-service-host-name-server.xml.i34
-rw-r--r--interface-definitions/include/firewall/action-and-notrack.xml.i41
-rw-r--r--interface-definitions/include/firewall/common-rule-inet.xml.i374
-rw-r--r--interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i331
-rw-r--r--interface-definitions/include/firewall/common-rule-ipv4.xml.i72
-rw-r--r--interface-definitions/include/firewall/common-rule-ipv6.xml.i72
-rw-r--r--interface-definitions/include/firewall/default-action-base-chains.xml.i22
-rw-r--r--interface-definitions/include/firewall/firewall-hashing-parameters.xml.i35
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i252
-rw-r--r--interface-definitions/include/firewall/inbound-interface.xml.i10
-rw-r--r--interface-definitions/include/firewall/ipv4-custom-name.xml.i41
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-forward.xml.i36
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-input.xml.i35
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-output.xml.i35
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-prerouting.xml.i85
-rw-r--r--interface-definitions/include/firewall/ipv6-custom-name.xml.i41
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-forward.xml.i36
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-input.xml.i35
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-output.xml.i35
-rw-r--r--interface-definitions/include/firewall/match-interface.xml.i23
-rw-r--r--interface-definitions/include/firewall/nat-balance.xml.i28
-rw-r--r--interface-definitions/include/firewall/outbound-interface.xml.i10
-rw-r--r--interface-definitions/include/interface/dhcpv6-options.xml.i6
-rw-r--r--interface-definitions/include/interface/eapol.xml.i2
-rw-r--r--interface-definitions/include/interface/ipv6-accept-dad.xml.i20
-rw-r--r--interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i3
-rw-r--r--interface-definitions/include/interface/ipv6-options.xml.i2
-rw-r--r--interface-definitions/include/interface/mac-multi.xml.i15
-rw-r--r--interface-definitions/include/interface/macsec-key.xml.i15
-rw-r--r--interface-definitions/include/interface/netns.xml.i2
-rw-r--r--interface-definitions/include/interface/parameters-innerproto.xml.i8
-rw-r--r--interface-definitions/include/interface/per-client-thread.xml.i8
-rw-r--r--interface-definitions/include/interface/xdp.xml.i8
-rw-r--r--interface-definitions/include/nat-rule.xml.i9
-rw-r--r--interface-definitions/include/ospf/graceful-restart.xml.i67
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i82
-rw-r--r--interface-definitions/include/ospfv3/protocol-common-config.xml.i15
-rw-r--r--interface-definitions/include/policy/extended-community-value-list.xml.i2
-rw-r--r--interface-definitions/include/policy/tag.xml.i14
-rw-r--r--interface-definitions/include/qos/bandwidth.xml.i2
-rw-r--r--interface-definitions/include/radius-server-auth-port.xml.i11
-rw-r--r--interface-definitions/include/radius-server-key.xml.i8
-rw-r--r--interface-definitions/include/version/dns-dynamic-version.xml.i3
-rw-r--r--interface-definitions/include/version/firewall-version.xml.i2
-rw-r--r--interface-definitions/include/version/interfaces-version.xml.i2
-rw-r--r--interface-definitions/include/version/ntp-version.xml.i2
-rw-r--r--interface-definitions/include/version/vrrp-version.xml.i2
-rw-r--r--interface-definitions/interfaces-bonding.xml.in1
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in1
-rw-r--r--interface-definitions/interfaces-geneve.xml.in1
-rw-r--r--interface-definitions/interfaces-macsec.xml.in22
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in23
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in4
-rw-r--r--interface-definitions/interfaces-wireless.xml.in44
-rw-r--r--interface-definitions/nat.xml.in8
-rw-r--r--interface-definitions/netns.xml.in2
-rw-r--r--interface-definitions/ntp.xml.in2
-rw-r--r--interface-definitions/policy.xml.in26
-rw-r--r--interface-definitions/protocols-mpls.xml.in2
-rw-r--r--interface-definitions/service-config-sync.xml.in104
-rw-r--r--interface-definitions/service-ids-ddos-protection.xml.in33
-rw-r--r--interface-definitions/service-mdns-repeater.xml.in1
-rw-r--r--interface-definitions/service-monitoring-zabbix-agent.xml.in193
-rw-r--r--interface-definitions/system-ip.xml.in58
-rw-r--r--interface-definitions/system-login.xml.in66
-rw-r--r--interface-definitions/system-option.xml.in28
-rw-r--r--interface-definitions/vpp.xml.in342
-rw-r--r--interface-definitions/xml-component-version.xml.in1
-rw-r--r--op-mode-definitions/clear-dhcp-server-lease.xml.in2
-rw-r--r--op-mode-definitions/configure.xml.in7
-rw-r--r--op-mode-definitions/container.xml.in2
-rw-r--r--op-mode-definitions/dhcp.xml.in24
-rw-r--r--op-mode-definitions/dns-dynamic.xml.in65
-rw-r--r--op-mode-definitions/dns-forwarding.xml.in10
-rw-r--r--op-mode-definitions/firewall.xml.in205
-rw-r--r--op-mode-definitions/include/frr-detail.xml.i (renamed from op-mode-definitions/include/ospfv3/detail.xml.i)6
-rw-r--r--op-mode-definitions/include/monitor-background.xml.i21
-rw-r--r--op-mode-definitions/include/ospf/common.xml.i (renamed from op-mode-definitions/include/ospf-common.xml.i)9
-rw-r--r--op-mode-definitions/include/ospf/graceful-restart.xml.i13
-rw-r--r--op-mode-definitions/include/ospfv3/adv-router-id-node-tag.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/adv-router.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/border-routers.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/database.xml.i30
-rw-r--r--op-mode-definitions/include/ospfv3/interface.xml.i8
-rw-r--r--op-mode-definitions/include/ospfv3/linkstate-id-node-tag.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/linkstate-id.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/linkstate.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/neighbor.xml.i2
-rw-r--r--op-mode-definitions/include/ospfv3/route.xml.i12
-rw-r--r--op-mode-definitions/include/ospfv3/self-originated.xml.i2
-rw-r--r--op-mode-definitions/monitor-bridge.xml.in4
-rw-r--r--op-mode-definitions/monitor-command.xml.in28
-rw-r--r--op-mode-definitions/monitor-log.xml.in153
-rw-r--r--op-mode-definitions/monitor-ndp.xml.in8
-rw-r--r--op-mode-definitions/monitor-protocol.xml.in42
-rw-r--r--op-mode-definitions/pki.xml.in42
-rw-r--r--op-mode-definitions/rpki.xml.in (renamed from op-mode-definitions/show-rpki.xml.in)10
-rw-r--r--op-mode-definitions/show-bridge.xml.in24
-rw-r--r--op-mode-definitions/show-evpn.xml.in28
-rw-r--r--op-mode-definitions/show-interfaces-bonding.xml.in6
-rw-r--r--op-mode-definitions/show-interfaces-ethernet.xml.in6
-rw-r--r--op-mode-definitions/show-ip-ospf.xml.in4
-rw-r--r--op-mode-definitions/show-ipv6-ospfv3.xml.in2
-rw-r--r--op-mode-definitions/show-log.xml.in125
-rw-r--r--op-mode-definitions/show-reverse-proxy.xml.in13
-rw-r--r--op-mode-definitions/webproxy.xml.in50
-rw-r--r--python/vyos/accel_ppp.py2
-rw-r--r--python/vyos/component_version.py2
-rw-r--r--python/vyos/config.py116
-rw-r--r--python/vyos/config_mgmt.py91
-rw-r--r--python/vyos/configdep.py2
-rw-r--r--python/vyos/configdict.py248
-rw-r--r--python/vyos/configdiff.py23
-rw-r--r--python/vyos/configquery.py8
-rw-r--r--python/vyos/configsession.py23
-rw-r--r--python/vyos/configsource.py4
-rw-r--r--python/vyos/configtree.py41
-rw-r--r--python/vyos/configverify.py23
-rw-r--r--python/vyos/defaults.py4
-rw-r--r--python/vyos/dicts.py53
-rw-r--r--python/vyos/ethtool.py5
-rw-r--r--python/vyos/firewall.py92
-rw-r--r--python/vyos/frr.py19
-rw-r--r--python/vyos/ifconfig/bond.py8
-rw-r--r--python/vyos/ifconfig/bridge.py8
-rw-r--r--python/vyos/ifconfig/control.py8
-rw-r--r--python/vyos/ifconfig/ethernet.py8
-rw-r--r--python/vyos/ifconfig/geneve.py3
-rw-r--r--python/vyos/ifconfig/interface.py211
-rw-r--r--python/vyos/ifconfig/l2tpv3.py5
-rw-r--r--python/vyos/ifconfig/macsec.py22
-rw-r--r--python/vyos/ifconfig/pppoe.py4
-rw-r--r--python/vyos/ifconfig/tunnel.py4
-rw-r--r--python/vyos/ifconfig/vrrp.py19
-rw-r--r--python/vyos/ifconfig/vti.py2
-rw-r--r--python/vyos/ifconfig/vxlan.py2
-rw-r--r--python/vyos/ifconfig/wireguard.py4
-rw-r--r--python/vyos/initialsetup.py14
-rw-r--r--python/vyos/ipsec.py16
-rw-r--r--python/vyos/migrator.py2
-rw-r--r--python/vyos/nat.py78
-rw-r--r--python/vyos/pki.py14
-rw-r--r--python/vyos/qos/base.py201
-rw-r--r--python/vyos/qos/limiter.py1
-rw-r--r--python/vyos/qos/priority.py2
-rw-r--r--python/vyos/qos/trafficshaper.py13
-rw-r--r--python/vyos/remote.py85
-rw-r--r--python/vyos/template.py25
-rw-r--r--python/vyos/util.py1160
-rw-r--r--python/vyos/utils/__init__.py30
-rw-r--r--python/vyos/utils/assertion.py81
-rw-r--r--python/vyos/utils/auth.py (renamed from python/vyos/authutils.py)2
-rw-r--r--python/vyos/utils/boot.py35
-rw-r--r--python/vyos/utils/commit.py60
-rw-r--r--python/vyos/utils/convert.py52
-rw-r--r--python/vyos/utils/dict.py101
-rw-r--r--python/vyos/utils/file.py12
-rw-r--r--python/vyos/utils/kernel.py38
-rw-r--r--python/vyos/utils/list.py20
-rw-r--r--python/vyos/utils/misc.py66
-rw-r--r--python/vyos/utils/network.py417
-rw-r--r--python/vyos/utils/permission.py78
-rw-r--r--python/vyos/utils/process.py232
-rw-r--r--python/vyos/utils/system.py107
-rw-r--r--python/vyos/validate.py259
-rw-r--r--python/vyos/version.py12
-rw-r--r--python/vyos/vpp.py315
-rw-r--r--python/vyos/xml_ref/__init__.py28
-rw-r--r--python/vyos/xml_ref/definition.py166
-rwxr-xr-xpython/vyos/xml_ref/generate_cache.py68
-rw-r--r--python/vyos/xml_ref/pkg_cache/__init__.py0
-rwxr-xr-xpython/vyos/xml_ref/update_cache.py51
-rwxr-xr-xscripts/update-configd-include-file2
-rw-r--r--smoketest/configs/dialup-router-complex4
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py8
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py159
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py20
-rwxr-xr-xsmoketest/scripts/cli/test_configd_init.py5
-rwxr-xr-xsmoketest/scripts/cli/test_container.py6
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py533
-rwxr-xr-xsmoketest/scripts/cli/test_ha_virtual_server.py44
-rwxr-xr-xsmoketest/scripts/cli/test_ha_vrrp.py9
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py11
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py13
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py17
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_geneve.py8
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_input.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_l2tpv3.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_loopback.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py106
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_netns.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py10
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_virtual_ethernet.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py4
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireguard.py58
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py57
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancing_reverse_proxy.py4
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancing_wan.py4
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py68
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py4
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py2
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py18
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py55
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_igmp-proxy.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py12
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_nhrp.py6
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py134
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py74
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rip.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ripng.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rpki.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static_arp.py2
-rwxr-xr-xsmoketest/scripts/cli/test_qos.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-relay.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-relay.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-server.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py247
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_forwarding.py6
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py6
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_ipoe-server.py2
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py6
-rwxr-xr-xsmoketest/scripts/cli/test_service_mdns-repeater.py37
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_telegraf.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_zabbix-agent.py89
-rwxr-xr-xsmoketest/scripts/cli/test_service_ntp.py6
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py2
-rwxr-xr-xsmoketest/scripts/cli/test_service_router-advert.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_salt.py8
-rwxr-xr-xsmoketest/scripts/cli/test_service_snmp.py8
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py24
-rwxr-xr-xsmoketest/scripts/cli/test_service_tftp-server.py8
-rwxr-xr-xsmoketest/scripts/cli/test_service_upnp.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_webproxy.py6
-rwxr-xr-xsmoketest/scripts/cli/test_system_conntrack.py6
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py6
-rwxr-xr-xsmoketest/scripts/cli/test_system_frr.py4
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py13
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py17
-rwxr-xr-xsmoketest/scripts/cli/test_system_lcd.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py50
-rwxr-xr-xsmoketest/scripts/cli/test_system_logs.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_nameserver.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_sflow.py6
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py8
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py4
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_sstp.py2
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py41
-rwxr-xr-xsmoketest/scripts/system/test_kernel_options.py5
-rwxr-xr-xsmoketest/scripts/system/test_module_load.py5
-rwxr-xr-xsrc/completion/list_dumpable_interfaces.py2
-rwxr-xr-xsrc/completion/list_ipoe.py16
-rw-r--r--src/completion/list_ipsec_profile_tunnels.py2
-rwxr-xr-xsrc/completion/list_openconnect_users.py2
-rwxr-xr-xsrc/completion/list_openvpn_users.py2
-rwxr-xr-xsrc/conf_mode/arp.py2
-rwxr-xr-xsrc/conf_mode/bcast_relay.py20
-rwxr-xr-xsrc/conf_mode/conntrack.py24
-rwxr-xr-xsrc/conf_mode/conntrack_sync.py20
-rwxr-xr-xsrc/conf_mode/container.py100
-rwxr-xr-xsrc/conf_mode/dhcp_relay.py20
-rwxr-xr-xsrc/conf_mode/dhcp_server.py28
-rwxr-xr-xsrc/conf_mode/dhcpv6_relay.py24
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py6
-rwxr-xr-xsrc/conf_mode/dns_dynamic.py134
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py69
-rwxr-xr-xsrc/conf_mode/dynamic_dns.py156
-rwxr-xr-xsrc/conf_mode/firewall.py283
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py54
-rwxr-xr-xsrc/conf_mode/high-availability.py47
-rwxr-xr-xsrc/conf_mode/host_name.py9
-rwxr-xr-xsrc/conf_mode/http-api.py14
-rwxr-xr-xsrc/conf_mode/https.py8
-rwxr-xr-xsrc/conf_mode/igmp_proxy.py19
-rwxr-xr-xsrc/conf_mode/intel_qat.py5
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py10
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py28
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py25
-rwxr-xr-xsrc/conf_mode/interfaces-l2tpv3.py4
-rwxr-xr-xsrc/conf_mode/interfaces-macsec.py80
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py125
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py4
-rwxr-xr-xsrc/conf_mode/interfaces-sstpc.py8
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py10
-rwxr-xr-xsrc/conf_mode/interfaces-vti.py2
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py4
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py13
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py61
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py13
-rwxr-xr-xsrc/conf_mode/le_cert.py6
-rwxr-xr-xsrc/conf_mode/lldp.py41
-rwxr-xr-xsrc/conf_mode/load-balancing-haproxy.py20
-rwxr-xr-xsrc/conf_mode/load-balancing-wan.py53
-rwxr-xr-xsrc/conf_mode/nat.py49
-rwxr-xr-xsrc/conf_mode/nat66.py18
-rwxr-xr-xsrc/conf_mode/netns.py6
-rwxr-xr-xsrc/conf_mode/ntp.py40
-rwxr-xr-xsrc/conf_mode/pki.py9
-rwxr-xr-xsrc/conf_mode/policy-local-route.py2
-rwxr-xr-xsrc/conf_mode/policy-route.py8
-rwxr-xr-xsrc/conf_mode/policy.py2
-rwxr-xr-xsrc/conf_mode/protocols_babel.py21
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py17
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py45
-rwxr-xr-xsrc/conf_mode/protocols_failover.py15
-rwxr-xr-xsrc/conf_mode/protocols_igmp.py5
-rwxr-xr-xsrc/conf_mode/protocols_isis.py18
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py6
-rwxr-xr-xsrc/conf_mode/protocols_nhrp.py6
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py86
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py25
-rwxr-xr-xsrc/conf_mode/protocols_pim.py5
-rwxr-xr-xsrc/conf_mode/protocols_rip.py7
-rwxr-xr-xsrc/conf_mode/protocols_ripng.py7
-rwxr-xr-xsrc/conf_mode/protocols_rpki.py7
-rwxr-xr-xsrc/conf_mode/protocols_static.py2
-rwxr-xr-xsrc/conf_mode/protocols_static_multicast.py2
-rwxr-xr-xsrc/conf_mode/qos.py115
-rwxr-xr-xsrc/conf_mode/salt-minion.py9
-rwxr-xr-xsrc/conf_mode/service_config_sync.py105
-rwxr-xr-xsrc/conf_mode/service_console-server.py10
-rwxr-xr-xsrc/conf_mode/service_event_handler.py3
-rwxr-xr-xsrc/conf_mode/service_ids_fastnetmon.py26
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py4
-rwxr-xr-xsrc/conf_mode/service_mdns-repeater.py2
-rwxr-xr-xsrc/conf_mode/service_monitoring_telegraf.py11
-rwxr-xr-xsrc/conf_mode/service_monitoring_zabbix-agent.py98
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py4
-rwxr-xr-xsrc/conf_mode/service_router-advert.py41
-rwxr-xr-xsrc/conf_mode/service_sla.py12
-rwxr-xr-xsrc/conf_mode/service_upnp.py9
-rwxr-xr-xsrc/conf_mode/service_webproxy.py27
-rwxr-xr-xsrc/conf_mode/snmp.py71
-rwxr-xr-xsrc/conf_mode/ssh.py8
-rwxr-xr-xsrc/conf_mode/system-ip.py62
-rwxr-xr-xsrc/conf_mode/system-ipv6.py38
-rwxr-xr-xsrc/conf_mode/system-login-banner.py2
-rwxr-xr-xsrc/conf_mode/system-login.py173
-rwxr-xr-xsrc/conf_mode/system-logs.py12
-rwxr-xr-xsrc/conf_mode/system-option.py31
-rwxr-xr-xsrc/conf_mode/system-syslog.py58
-rwxr-xr-xsrc/conf_mode/system-timezone.py2
-rwxr-xr-xsrc/conf_mode/system_console.py18
-rwxr-xr-xsrc/conf_mode/system_frr.py6
-rwxr-xr-xsrc/conf_mode/system_lcd.py4
-rwxr-xr-xsrc/conf_mode/system_sflow.py29
-rwxr-xr-xsrc/conf_mode/system_sysctl.py2
-rwxr-xr-xsrc/conf_mode/system_update_check.py2
-rwxr-xr-xsrc/conf_mode/tftp_server.py16
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py100
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py8
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py99
-rwxr-xr-xsrc/conf_mode/vpn_pptp.py3
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py10
-rwxr-xr-xsrc/conf_mode/vpp.py207
-rwxr-xr-xsrc/conf_mode/vrf.py35
-rw-r--r--src/conf_mode/vrf_vni.py2
-rw-r--r--src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook (renamed from src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook)3
-rwxr-xr-xsrc/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook16
-rwxr-xr-xsrc/etc/ipsec.d/vti-up-down6
-rw-r--r--src/etc/modprobe.d/openvpn.conf1
-rwxr-xr-xsrc/etc/netplug/linkdown.d/dhclient65
-rwxr-xr-xsrc/etc/netplug/linkup.d/dhclient64
-rwxr-xr-xsrc/etc/netplug/linkup.d/vyos-python-helper4
-rwxr-xr-xsrc/etc/netplug/netplug41
-rw-r--r--src/etc/netplug/netplugd.conf4
-rwxr-xr-xsrc/etc/netplug/vyos-netplug-dhcp-client62
-rwxr-xr-xsrc/etc/opennhrp/opennhrp-script.py4
-rw-r--r--src/etc/rsyslog.conf4
-rwxr-xr-xsrc/etc/systemd/system-generators/vyos-generator94
-rw-r--r--src/etc/systemd/system/ddclient.service.d/override.conf11
-rw-r--r--src/etc/systemd/system/frr.service.d/override.conf4
-rw-r--r--src/etc/systemd/system/getty@.service.d/aftervyos.conf3
-rw-r--r--src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf3
-rwxr-xr-xsrc/etc/telegraf/custom_scripts/show_firewall_input_filter.py2
-rwxr-xr-xsrc/etc/telegraf/custom_scripts/vyos_services_input_filter.py5
-rwxr-xr-xsrc/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py6
-rwxr-xr-xsrc/helpers/commit-confirm-notify.py1
-rwxr-xr-xsrc/helpers/run-config-migration.py2
-rwxr-xr-xsrc/helpers/vyos-boot-config-loader.py2
-rwxr-xr-xsrc/helpers/vyos-check-wwan.py2
-rwxr-xr-xsrc/helpers/vyos-domain-resolver.py20
-rwxr-xr-xsrc/helpers/vyos-failover.py2
-rwxr-xr-xsrc/helpers/vyos-interface-rescan.py2
-rwxr-xr-xsrc/helpers/vyos-merge-config.py7
-rwxr-xr-xsrc/helpers/vyos-save-config.py55
-rwxr-xr-xsrc/helpers/vyos-sudo.py2
-rwxr-xr-xsrc/helpers/vyos_config_sync.py192
-rwxr-xr-xsrc/helpers/vyos_net_name7
-rwxr-xr-xsrc/init/vyos-config16
-rwxr-xr-xsrc/init/vyos-router445
-rwxr-xr-xsrc/migration-scripts/bgp/0-to-12
-rwxr-xr-xsrc/migration-scripts/bgp/1-to-22
-rwxr-xr-xsrc/migration-scripts/bgp/2-to-32
-rwxr-xr-xsrc/migration-scripts/bgp/3-to-42
-rwxr-xr-xsrc/migration-scripts/config-management/0-to-12
-rwxr-xr-xsrc/migration-scripts/conntrack-sync/1-to-22
-rwxr-xr-xsrc/migration-scripts/conntrack/1-to-22
-rwxr-xr-xsrc/migration-scripts/conntrack/2-to-32
-rwxr-xr-xsrc/migration-scripts/container/0-to-112
-rwxr-xr-xsrc/migration-scripts/dhcp-relay/1-to-22
-rwxr-xr-xsrc/migration-scripts/dhcp-server/4-to-52
-rwxr-xr-xsrc/migration-scripts/dhcp-server/5-to-62
-rwxr-xr-xsrc/migration-scripts/dhcpv6-server/0-to-12
-rwxr-xr-xsrc/migration-scripts/dns-dynamic/0-to-1104
-rwxr-xr-xsrc/migration-scripts/dns-forwarding/0-to-12
-rwxr-xr-xsrc/migration-scripts/dns-forwarding/1-to-22
-rwxr-xr-xsrc/migration-scripts/dns-forwarding/2-to-32
-rwxr-xr-xsrc/migration-scripts/dns-forwarding/3-to-42
-rwxr-xr-xsrc/migration-scripts/firewall/10-to-11374
-rwxr-xr-xsrc/migration-scripts/firewall/5-to-648
-rwxr-xr-xsrc/migration-scripts/firewall/6-to-72
-rwxr-xr-xsrc/migration-scripts/firewall/7-to-82
-rwxr-xr-xsrc/migration-scripts/firewall/8-to-92
-rwxr-xr-xsrc/migration-scripts/firewall/9-to-102
-rwxr-xr-xsrc/migration-scripts/flow-accounting/0-to-12
-rwxr-xr-xsrc/migration-scripts/https/2-to-32
-rwxr-xr-xsrc/migration-scripts/https/3-to-42
-rwxr-xr-xsrc/migration-scripts/ids/0-to-12
-rwxr-xr-xsrc/migration-scripts/interfaces/0-to-12
-rwxr-xr-xsrc/migration-scripts/interfaces/1-to-22
-rwxr-xr-xsrc/migration-scripts/interfaces/10-to-112
-rwxr-xr-xsrc/migration-scripts/interfaces/11-to-122
-rwxr-xr-xsrc/migration-scripts/interfaces/12-to-132
-rwxr-xr-xsrc/migration-scripts/interfaces/13-to-142
-rwxr-xr-xsrc/migration-scripts/interfaces/14-to-152
-rwxr-xr-xsrc/migration-scripts/interfaces/15-to-162
-rwxr-xr-xsrc/migration-scripts/interfaces/16-to-176
-rwxr-xr-xsrc/migration-scripts/interfaces/17-to-182
-rwxr-xr-xsrc/migration-scripts/interfaces/18-to-192
-rwxr-xr-xsrc/migration-scripts/interfaces/19-to-202
-rwxr-xr-xsrc/migration-scripts/interfaces/2-to-32
-rwxr-xr-xsrc/migration-scripts/interfaces/20-to-212
-rwxr-xr-xsrc/migration-scripts/interfaces/21-to-222
-rwxr-xr-xsrc/migration-scripts/interfaces/22-to-232
-rwxr-xr-xsrc/migration-scripts/interfaces/23-to-242
-rwxr-xr-xsrc/migration-scripts/interfaces/24-to-2513
-rwxr-xr-xsrc/migration-scripts/interfaces/25-to-262
-rwxr-xr-xsrc/migration-scripts/interfaces/26-to-272
-rwxr-xr-xsrc/migration-scripts/interfaces/27-to-283
-rwxr-xr-xsrc/migration-scripts/interfaces/28-to-2949
-rwxr-xr-xsrc/migration-scripts/interfaces/29-to-3054
-rwxr-xr-xsrc/migration-scripts/interfaces/3-to-42
-rwxr-xr-xsrc/migration-scripts/interfaces/4-to-52
-rwxr-xr-xsrc/migration-scripts/interfaces/5-to-62
-rwxr-xr-xsrc/migration-scripts/interfaces/6-to-72
-rwxr-xr-xsrc/migration-scripts/interfaces/7-to-87
-rwxr-xr-xsrc/migration-scripts/interfaces/8-to-92
-rwxr-xr-xsrc/migration-scripts/interfaces/9-to-102
-rwxr-xr-xsrc/migration-scripts/ipoe-server/0-to-12
-rwxr-xr-xsrc/migration-scripts/ipsec/10-to-112
-rwxr-xr-xsrc/migration-scripts/ipsec/11-to-122
-rwxr-xr-xsrc/migration-scripts/ipsec/4-to-52
-rwxr-xr-xsrc/migration-scripts/ipsec/5-to-62
-rwxr-xr-xsrc/migration-scripts/ipsec/6-to-710
-rwxr-xr-xsrc/migration-scripts/ipsec/7-to-82
-rwxr-xr-xsrc/migration-scripts/ipsec/8-to-92
-rwxr-xr-xsrc/migration-scripts/ipsec/9-to-102
-rwxr-xr-xsrc/migration-scripts/isis/0-to-17
-rwxr-xr-xsrc/migration-scripts/isis/1-to-22
-rwxr-xr-xsrc/migration-scripts/isis/2-to-32
-rwxr-xr-xsrc/migration-scripts/l2tp/0-to-12
-rwxr-xr-xsrc/migration-scripts/l2tp/1-to-22
-rwxr-xr-xsrc/migration-scripts/l2tp/2-to-32
-rwxr-xr-xsrc/migration-scripts/l2tp/3-to-48
-rwxr-xr-xsrc/migration-scripts/lldp/0-to-12
-rwxr-xr-xsrc/migration-scripts/monitoring/0-to-12
-rwxr-xr-xsrc/migration-scripts/nat/4-to-52
-rwxr-xr-xsrc/migration-scripts/nat66/0-to-12
-rwxr-xr-xsrc/migration-scripts/ntp/0-to-12
-rwxr-xr-xsrc/migration-scripts/ntp/1-to-22
-rwxr-xr-xsrc/migration-scripts/ntp/2-to-362
-rwxr-xr-xsrc/migration-scripts/openconnect/0-to-18
-rwxr-xr-xsrc/migration-scripts/openconnect/1-to-216
-rwxr-xr-xsrc/migration-scripts/ospf/0-to-12
-rwxr-xr-xsrc/migration-scripts/ospf/1-to-22
-rwxr-xr-xsrc/migration-scripts/policy/0-to-12
-rwxr-xr-xsrc/migration-scripts/policy/1-to-22
-rwxr-xr-xsrc/migration-scripts/policy/2-to-32
-rwxr-xr-xsrc/migration-scripts/policy/3-to-410
-rwxr-xr-xsrc/migration-scripts/policy/4-to-52
-rwxr-xr-xsrc/migration-scripts/pppoe-server/0-to-12
-rwxr-xr-xsrc/migration-scripts/pppoe-server/1-to-22
-rwxr-xr-xsrc/migration-scripts/pppoe-server/2-to-32
-rwxr-xr-xsrc/migration-scripts/pppoe-server/3-to-42
-rwxr-xr-xsrc/migration-scripts/pppoe-server/4-to-52
-rwxr-xr-xsrc/migration-scripts/pppoe-server/5-to-62
-rwxr-xr-xsrc/migration-scripts/pptp/0-to-12
-rwxr-xr-xsrc/migration-scripts/pptp/1-to-22
-rwxr-xr-xsrc/migration-scripts/qos/1-to-24
-rwxr-xr-xsrc/migration-scripts/quagga/10-to-112
-rwxr-xr-xsrc/migration-scripts/quagga/2-to-32
-rwxr-xr-xsrc/migration-scripts/quagga/3-to-42
-rwxr-xr-xsrc/migration-scripts/quagga/4-to-52
-rwxr-xr-xsrc/migration-scripts/quagga/5-to-62
-rwxr-xr-xsrc/migration-scripts/quagga/6-to-72
-rwxr-xr-xsrc/migration-scripts/quagga/7-to-82
-rwxr-xr-xsrc/migration-scripts/quagga/8-to-92
-rwxr-xr-xsrc/migration-scripts/quagga/9-to-102
-rwxr-xr-xsrc/migration-scripts/rip/0-to-12
-rwxr-xr-xsrc/migration-scripts/rpki/0-to-12
-rwxr-xr-xsrc/migration-scripts/salt/0-to-12
-rwxr-xr-xsrc/migration-scripts/snmp/0-to-12
-rwxr-xr-xsrc/migration-scripts/snmp/1-to-22
-rwxr-xr-xsrc/migration-scripts/snmp/2-to-32
-rwxr-xr-xsrc/migration-scripts/ssh/0-to-12
-rwxr-xr-xsrc/migration-scripts/ssh/1-to-22
-rwxr-xr-xsrc/migration-scripts/sstp/0-to-12
-rwxr-xr-xsrc/migration-scripts/sstp/1-to-22
-rwxr-xr-xsrc/migration-scripts/sstp/2-to-32
-rwxr-xr-xsrc/migration-scripts/sstp/3-to-48
-rwxr-xr-xsrc/migration-scripts/system/10-to-112
-rwxr-xr-xsrc/migration-scripts/system/11-to-122
-rwxr-xr-xsrc/migration-scripts/system/12-to-132
-rwxr-xr-xsrc/migration-scripts/system/13-to-144
-rwxr-xr-xsrc/migration-scripts/system/14-to-152
-rwxr-xr-xsrc/migration-scripts/system/15-to-162
-rwxr-xr-xsrc/migration-scripts/system/16-to-172
-rwxr-xr-xsrc/migration-scripts/system/17-to-182
-rwxr-xr-xsrc/migration-scripts/system/18-to-195
-rwxr-xr-xsrc/migration-scripts/system/19-to-202
-rwxr-xr-xsrc/migration-scripts/system/20-to-212
-rwxr-xr-xsrc/migration-scripts/system/21-to-222
-rwxr-xr-xsrc/migration-scripts/system/22-to-232
-rwxr-xr-xsrc/migration-scripts/system/23-to-242
-rwxr-xr-xsrc/migration-scripts/system/24-to-252
-rwxr-xr-xsrc/migration-scripts/system/25-to-262
-rwxr-xr-xsrc/migration-scripts/system/6-to-72
-rwxr-xr-xsrc/migration-scripts/system/7-to-82
-rwxr-xr-xsrc/migration-scripts/system/8-to-92
-rwxr-xr-xsrc/migration-scripts/vrf/0-to-12
-rwxr-xr-xsrc/migration-scripts/vrf/1-to-22
-rwxr-xr-xsrc/migration-scripts/vrf/2-to-32
-rwxr-xr-xsrc/migration-scripts/vrrp/1-to-22
-rwxr-xr-xsrc/migration-scripts/vrrp/2-to-32
-rwxr-xr-xsrc/migration-scripts/vrrp/3-to-451
-rwxr-xr-xsrc/migration-scripts/webproxy/1-to-22
-rwxr-xr-xsrc/op_mode/accelppp.py2
-rwxr-xr-xsrc/op_mode/bgp.py6
-rwxr-xr-xsrc/op_mode/bridge.py82
-rwxr-xr-xsrc/op_mode/clear_conntrack.py5
-rwxr-xr-xsrc/op_mode/clear_dhcp_lease.py6
-rwxr-xr-xsrc/op_mode/connect_disconnect.py10
-rwxr-xr-xsrc/op_mode/conntrack.py6
-rwxr-xr-xsrc/op_mode/conntrack_sync.py8
-rwxr-xr-xsrc/op_mode/container.py10
-rwxr-xr-xsrc/op_mode/dhcp.py102
-rwxr-xr-xsrc/op_mode/dns.py2
-rwxr-xr-xsrc/op_mode/dns_dynamic.py (renamed from src/op_mode/dynamic_dns.py)4
-rwxr-xr-xsrc/op_mode/dns_forwarding_reset.py2
-rwxr-xr-xsrc/op_mode/dns_forwarding_statistics.py2
-rwxr-xr-xsrc/op_mode/firewall.py294
-rwxr-xr-xsrc/op_mode/flow_accounting_op.py10
-rwxr-xr-xsrc/op_mode/format_disk.py8
-rwxr-xr-xsrc/op_mode/generate_interfaces_debug_archive.py2
-rwxr-xr-xsrc/op_mode/generate_ipsec_debug_archive.py2
-rwxr-xr-xsrc/op_mode/generate_openconnect_otp_key.py4
-rwxr-xr-xsrc/op_mode/generate_ovpn_client_file.py2
-rwxr-xr-xsrc/op_mode/generate_ssh_server_key.py6
-rwxr-xr-xsrc/op_mode/generate_system_login_user.py4
-rwxr-xr-xsrc/op_mode/igmp-proxy.py10
-rwxr-xr-xsrc/op_mode/ikev2_profile_generator.py2
-rwxr-xr-xsrc/op_mode/interfaces.py8
-rwxr-xr-xsrc/op_mode/ipoe-control.py5
-rwxr-xr-xsrc/op_mode/ipsec.py6
-rwxr-xr-xsrc/op_mode/lldp.py4
-rwxr-xr-xsrc/op_mode/log.py2
-rwxr-xr-xsrc/op_mode/memory.py2
-rwxr-xr-xsrc/op_mode/nat.py4
-rwxr-xr-xsrc/op_mode/neighbor.py12
-rwxr-xr-xsrc/op_mode/nhrp.py6
-rwxr-xr-xsrc/op_mode/openconnect-control.py11
-rwxr-xr-xsrc/op_mode/openconnect.py2
-rwxr-xr-xsrc/op_mode/openvpn.py8
-rwxr-xr-xsrc/op_mode/ping.py12
-rwxr-xr-xsrc/op_mode/pki.py61
-rwxr-xr-xsrc/op_mode/policy_route.py8
-rwxr-xr-xsrc/op_mode/powerctrl.py19
-rwxr-xr-xsrc/op_mode/ppp-server-ctrl.py5
-rwxr-xr-xsrc/op_mode/reset_openvpn.py4
-rwxr-xr-xsrc/op_mode/reset_vpn.py4
-rwxr-xr-xsrc/op_mode/restart_dhcp_relay.py4
-rwxr-xr-xsrc/op_mode/restart_frr.py10
-rwxr-xr-xsrc/op_mode/reverseproxy.py239
-rwxr-xr-xsrc/op_mode/route.py4
-rwxr-xr-xsrc/op_mode/sflow.py2
-rwxr-xr-xsrc/op_mode/show-bond.py2
-rwxr-xr-xsrc/op_mode/show_acceleration.py6
-rwxr-xr-xsrc/op_mode/show_ntp.sh2
-rwxr-xr-xsrc/op_mode/show_openconnect_otp.py42
-rwxr-xr-xsrc/op_mode/show_openvpn_mfa.py6
-rwxr-xr-xsrc/op_mode/show_sensors.py22
-rw-r--r--src/op_mode/show_techsupport_report.py5
-rwxr-xr-xsrc/op_mode/show_virtual_server.py2
-rwxr-xr-xsrc/op_mode/show_wireless.py4
-rwxr-xr-xsrc/op_mode/show_wwan.py2
-rwxr-xr-xsrc/op_mode/show_xdp_stats.sh7
-rwxr-xr-xsrc/op_mode/snmp.py2
-rwxr-xr-xsrc/op_mode/snmp_ifmib.py4
-rwxr-xr-xsrc/op_mode/storage.py4
-rwxr-xr-xsrc/op_mode/traceroute.py2
-rwxr-xr-xsrc/op_mode/uptime.py8
-rwxr-xr-xsrc/op_mode/vpn_ike_sa.py6
-rwxr-xr-xsrc/op_mode/vpn_ipsec.py2
-rwxr-xr-xsrc/op_mode/vrf.py25
-rwxr-xr-xsrc/op_mode/vrrp.py11
-rwxr-xr-xsrc/op_mode/vyos-op-cmd-wrapper.sh6
-rwxr-xr-xsrc/op_mode/webproxy_update_blacklist.sh9
-rwxr-xr-xsrc/op_mode/wireguard_client.py6
-rwxr-xr-xsrc/op_mode/zone.py4
-rw-r--r--src/pam-configs/radius11
-rw-r--r--src/pam-configs/tacplus17
-rwxr-xr-xsrc/services/api/graphql/generate/schema_from_op_mode.py2
-rw-r--r--src/services/api/graphql/libs/op_mode.py2
-rwxr-xr-xsrc/services/vyos-configd9
-rwxr-xr-xsrc/services/vyos-hostsd9
-rwxr-xr-xsrc/services/vyos-http-api-server234
-rwxr-xr-xsrc/system/keepalived-fifo.py6
-rwxr-xr-xsrc/system/vyos-event-handler.py7
-rwxr-xr-xsrc/system/vyos-system-update-check.py2
-rw-r--r--src/systemd/dhclient@.service13
-rw-r--r--src/systemd/dhcp6c@.service14
-rw-r--r--src/systemd/isc-dhcp-relay6.service4
-rw-r--r--src/systemd/vyos-router.service19
-rw-r--r--src/systemd/vyos.target3
-rw-r--r--src/tests/test_configverify.py2
-rw-r--r--src/tests/test_dict_search.py4
-rwxr-xr-xsrc/tests/test_find_device_file.py2
-rw-r--r--src/tests/test_initial_setup.py6
-rw-r--r--src/tests/test_utils.py (renamed from src/tests/test_util.py)8
-rw-r--r--src/tests/test_utils_network.py50
-rw-r--r--src/tests/test_validate.py50
-rwxr-xr-xsrc/validators/bgp-extended-community51
-rwxr-xr-xsrc/validators/ipv6-link-local2
-rwxr-xr-xsrc/validators/port-multi2
-rwxr-xr-xsrc/validators/port-range2
-rwxr-xr-xsrc/validators/script6
-rwxr-xr-xsrc/validators/timezone2
-rw-r--r--src/xdp/.gitignore5
-rw-r--r--src/xdp/Makefile12
-rw-r--r--src/xdp/common/Makefile23
-rw-r--r--src/xdp/common/README.org8
-rw-r--r--src/xdp/common/common.mk103
-rw-r--r--src/xdp/common/common_defines.h38
-rw-r--r--src/xdp/common/common_libbpf.c161
-rw-r--r--src/xdp/common/common_libbpf.h24
-rw-r--r--src/xdp/common/common_params.c197
-rw-r--r--src/xdp/common/common_params.h22
-rw-r--r--src/xdp/common/common_user_bpf_xdp.c389
-rw-r--r--src/xdp/common/common_user_bpf_xdp.h20
-rw-r--r--src/xdp/common/parsing_helpers.h244
-rw-r--r--src/xdp/common/rewrite_helpers.h144
-rw-r--r--src/xdp/common/xdp_stats_kern.h44
-rw-r--r--src/xdp/common/xdp_stats_kern_user.h19
-rw-r--r--src/xdp/include/bpf_endian.h58
-rw-r--r--src/xdp/include/bpf_legacy.h38
-rw-r--r--src/xdp/include/bpf_util.h61
-rw-r--r--src/xdp/include/jhash.h172
-rw-r--r--src/xdp/include/linux/bpf.h3278
-rw-r--r--src/xdp/include/linux/err.h33
-rw-r--r--src/xdp/include/linux/if_link.h1025
-rw-r--r--src/xdp/include/linux/if_xdp.h108
-rw-r--r--src/xdp/include/perf-sys.h77
-rw-r--r--src/xdp/utils/Makefile11
-rw-r--r--src/xdp/utils/xdp_loader.c165
-rw-r--r--src/xdp/utils/xdp_stats.c298
-rw-r--r--src/xdp/xdp_prog_kern.c347
-rw-r--r--src/xdp/xdp_prog_user.c182
740 files changed, 13757 insertions, 14599 deletions
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 000000000..e0b9ee430
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,12 @@
+equuleus:
+ - any:
+ - base-branch: 'equuleus'
+current:
+ - any:
+ - base-branch: 'current'
+crux:
+ - any:
+ - base-branch: 'crux'
+sagitta:
+ - any:
+ - base-branch: 'sagitta'
diff --git a/.github/workflows/pr-conflicts.yml b/.github/workflows/pr-conflicts.yml
index 72ff3969b..96040cd60 100644
--- a/.github/workflows/pr-conflicts.yml
+++ b/.github/workflows/pr-conflicts.yml
@@ -6,7 +6,7 @@ on:
jobs:
Conflict_Check:
name: 'Check PR status: conflicts and resolution'
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-22.04
steps:
- name: check if PRs are dirty
uses: eps1lon/actions-label-merge-conflict@releases/2.x
diff --git a/.github/workflows/pull-request-labels.yml b/.github/workflows/pull-request-labels.yml
new file mode 100644
index 000000000..778daae30
--- /dev/null
+++ b/.github/workflows/pull-request-labels.yml
@@ -0,0 +1,20 @@
+---
+name: Add pull request labels
+
+on:
+ pull_request_target:
+ branches:
+ - current
+ - crux
+ - equuleus
+ - sagitta
+
+jobs:
+ add-pr-label:
+ name: Add PR Labels
+ runs-on: ubuntu-20.04
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - uses: actions/labeler@v5.0.0-alpha.1
diff --git a/.gitignore b/.gitignore
index fe92f5b9d..7707e94ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,10 @@ debian/*.substvars
*.vpwhist
*.vtg
+# VS Code
+.vscode/*
+!.vscode/settings.json
+
# VIM
*.swp
@@ -140,3 +144,4 @@ debian/*.substvars
data/component-versions.json
# vyos-1x XML cache
python/vyos/xml_ref/cache.py
+python/vyos/xml_ref/pkg_cache/*_cache.py
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..caa87ba4a
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,18 @@
+{
+ "files.trimTrailingWhitespace": true,
+ "editor.tabSize": 4,
+ "editor.insertSpaces": true,
+ "files.insertFinalNewline": true,
+ "files.eol": "\n",
+ # https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers
+ "files.associations": {
+ "*.xml.i": "xml",
+ "*.xml.in": "xml",
+ "*.j2": "jinja",
+ },
+ "editor.indentSize": "tabSize",
+ "[jinja]": {
+ "editor.tabSize": 4,
+ "editor.wordBasedSuggestions": false
+ }
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3ff00df88..48cdbd5a7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -21,7 +21,7 @@ file(s) history by invoking git log path/to/file.txt.
In a big system, such as VyOS, that is comprised of multiple components, it’s
impossible to keep track of all the changes and bugs/feature requests in one’s
-head. We use a bugtracker known as Phabricator for it (“issue tracker” would
+head. We use a bugtracker named Phorge (formerly known Phabricator) for it (“issue tracker” would
be a better term, but this one stuck).
The information is used in three ways:
@@ -35,14 +35,14 @@ The information is used in three ways:
To make this approach work, every change must be associated with a task number
(prefixed with **T**) and a component. If there is no bug report/feature
-request for the changes you are going to make, you have to create a Phabricator
-task first. Once there is an entry in Phabricator, you should reference its id
+request for the changes you are going to make, you have to create a development portal
+task first. Once there is an entry in the development portal, you should reference its id
in your commit message, as shown below:
* `ddclient: T1030: auto create runtime directories`
* `Jenkins: add current Git commit ID to build description`
-If there is no [Phabricator](https://vyos.dev) reference in the
+If there is no [development portal](https://vyos.dev) reference in the
commits of your pull request, we have to ask you to amend the commit message.
Otherwise we will have to reject it.
@@ -57,7 +57,7 @@ development environments.
* A single, short, summary of the commit (recommended 50 characters or less,
not exceeding 80 characters) containing a prefix of the changed component
- and the corresponding Phabricator reference e.g. `snmp: T1111:` or
+ and the corresponding development portal reference e.g. `snmp: T1111:` or
`ethernet: T2222:` - multiple components could be concatenated as in `snmp:
ethernet: T3333`
* In some contexts, the first line is treated as the subject of an email and
@@ -126,7 +126,7 @@ also contain information that is helpful for the development team.
### Reporting
In order to open up a bug-report/feature request you need to create yourself
-an account on [Phabricator](https://vyos.dev). On the left
+an account in the [development portal](https://vyos.dev). On the left
side of the specific project (VyOS 1.2 or VyOS 1.3) you will find quick-links
for opening a bug-report/feature request.
@@ -141,7 +141,7 @@ for opening a bug-report/feature request.
You have an idea of how to make VyOS better or you are in need of a specific
feature which all users of VyOS would benefit from? To send a feature request
-please search [Phabricator](https://vyos.dev) if there is already a
+please search the [development portal](https://vyos.dev) if there is already a
request pending. You can enhance it or if you don't find one, create a new one
by use the quick link in the left side under the specific project.
diff --git a/Makefile b/Makefile
index 37499c178..b75a78784 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,6 @@ OP_TMPL_DIR := templates-op
BUILD_DIR := build
DATA_DIR := data
SHIM_DIR := src/shim
-XDP_DIR := src/xdp
LIBS := -lzmq
CFLAGS :=
BUILD_ARCH := $(shell dpkg-architecture -q DEB_BUILD_ARCH)
@@ -27,7 +26,7 @@ interface_definitions: $(config_xml_obj)
$(CURDIR)/scripts/override-default $(BUILD_DIR)/interface-definitions
- $(CURDIR)/python/vyos/xml_ref/generate_cache.py --xml-dir $(BUILD_DIR)/interface-definitions
+ $(CURDIR)/python/vyos/xml_ref/generate_cache.py --xml-dir $(BUILD_DIR)/interface-definitions || exit 1
find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1
@@ -62,10 +61,7 @@ op_mode_definitions: $(op_xml_obj)
rm -f $(OP_TMPL_DIR)/clear/node.def
rm -f $(OP_TMPL_DIR)/delete/node.def
rm -f $(OP_TMPL_DIR)/generate/node.def
- rm -f $(OP_TMPL_DIR)/monitor/node.def
rm -f $(OP_TMPL_DIR)/set/node.def
- rm -f $(OP_TMPL_DIR)/show/node.def
- rm -f $(OP_TMPL_DIR)/show/system/node.def
rm -f $(OP_TMPL_DIR)/show/tech-support/node.def
# XXX: ping and traceroute must be able to recursivly call itself as the
@@ -81,10 +77,6 @@ op_mode_definitions: $(op_xml_obj)
vyshim:
$(MAKE) -C $(SHIM_DIR)
-.PHONY: vyxdp
-vyxdp:
- $(MAKE) -C $(XDP_DIR)
-
.PHONY: all
all: clean interface_definitions op_mode_definitions check test j2lint vyshim
@@ -105,7 +97,6 @@ clean:
rm -rf $(TMPL_DIR)
rm -rf $(OP_TMPL_DIR)
$(MAKE) -C $(SHIM_DIR) clean
- $(MAKE) -C $(XDP_DIR) clean
.PHONY: test
test:
diff --git a/README.md b/README.md
index cc6c4e319..255d77846 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,5 @@
# vyos-1x: VyOS command definitions, configuration scripts, and data
-[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=vyos_vyos-1x&metric=coverage)](https://sonarcloud.io/component_measures?id=vyos_vyos-1x&metric=coverage)
-[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvyos%2Fvyos-1x.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvyos%2Fvyos-1x?ref=badge_shield)
-
VyOS 1.1.x had its codebase split into way too many submodules for no good
reason, which made it hard to navigate or write meaningful changelogs. As the
code undergoes rewrite in the new style in VyOS 1.2.0+, we consolidate the
@@ -56,7 +53,7 @@ The guidelines in a nutshell:
for the common structure
* Use the `get_config_dict()` API as much as possible when retrieving values from the CLI
* Use a template processor when the format is more complex than just one line
- (Jinja2 and pystache are acceptable options)
+ (our standard is Jinja2)
## Tests
@@ -74,5 +71,3 @@ Runtime tests are executed by the CI system on a running VyOS instance inside
QEMU. The testcases can be found inside the smoketest subdirectory which will
be placed into the vyos-1x-smoketest package.
-## License
-[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvyos%2Fvyos-1x.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvyos%2Fvyos-1x?ref=badge_large)
diff --git a/data/config-mode-dependencies.json b/data/config-mode-dependencies.json
index ccee359d1..91a757c16 100644
--- a/data/config-mode-dependencies.json
+++ b/data/config-mode-dependencies.json
@@ -28,5 +28,8 @@
"wireguard": ["interfaces-wireguard"],
"wireless": ["interfaces-wireless"],
"wwan": ["interfaces-wwan"]
+ },
+ "vpp": {
+ "ethernet": ["interfaces-ethernet"]
}
}
diff --git a/data/configd-include.json b/data/configd-include.json
index 456211caa..84bc1f14e 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -9,12 +9,11 @@
"dhcpv6_relay.py",
"dhcpv6_server.py",
"dns_forwarding.py",
-"dynamic_dns.py",
+"dns_dynamic.py",
"firewall.py",
"flow_accounting_conf.py",
"high-availability.py",
"host_name.py",
-"https.py",
"igmp_proxy.py",
"intel_qat.py",
"interfaces-bonding.py",
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json
index c7c67198e..042c466ab 100644
--- a/data/op-mode-standardized.json
+++ b/data/op-mode-standardized.json
@@ -18,6 +18,7 @@
"openconnect.py",
"openvpn.py",
"reset_vpn.py",
+"reverseproxy.py",
"route.py",
"system.py",
"ipsec.py",
diff --git a/data/templates/accel-ppp/ipoe.config.j2 b/data/templates/accel-ppp/ipoe.config.j2
index add3dc7e4..f59428509 100644
--- a/data/templates/accel-ppp/ipoe.config.j2
+++ b/data/templates/accel-ppp/ipoe.config.j2
@@ -36,7 +36,9 @@ verbose=1
{% set shared = 'shared=0,' %}
{% endif %}
{% set range = 'range=' ~ iface_config.client_subnet ~ ',' if iface_config.client_subnet is vyos_defined else '' %}
-{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,{{ range }}start=dhcpv4,ipv6=1
+{% set relay = ',' ~ 'relay=' ~ iface_config.external_dhcp.dhcp_relay if iface_config.external_dhcp.dhcp_relay is vyos_defined else '' %}
+{% set giaddr = ',' ~ 'giaddr=' ~ iface_config.external_dhcp.giaddr if iface_config.external_dhcp.giaddr is vyos_defined else '' %}
+{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,{{ range }}start=dhcpv4,ipv6=1{{ relay }}{{ giaddr }}
{% if iface_config.vlan is vyos_defined %}
vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
{% endif %}
diff --git a/data/templates/chrony/chrony.conf.j2 b/data/templates/chrony/chrony.conf.j2
index 7a36fe69d..0daec8fb8 100644
--- a/data/templates/chrony/chrony.conf.j2
+++ b/data/templates/chrony/chrony.conf.j2
@@ -53,8 +53,6 @@ bindaddress {{ address }}
{% endfor %}
{% endif %}
{% if interface is vyos_defined %}
-{% for ifname in interface %}
-binddevice {{ ifname }}
-{% endfor %}
+binddevice {{ interface }}
{% endif %}
{% endif %}
diff --git a/data/templates/dhcp-client/daemon-options.j2 b/data/templates/dhcp-client/daemon-options.j2
deleted file mode 100644
index b21ad08ab..000000000
--- a/data/templates/dhcp-client/daemon-options.j2
+++ /dev/null
@@ -1,4 +0,0 @@
-### Autogenerated by interface.py ###
-{% set if_metric = '-e IF_METRIC=' ~ dhcp_options.default_route_distance if dhcp_options.default_route_distance is vyos_defined else '' %}
-DHCLIENT_OPTS="-nw -cf /var/lib/dhcp/dhclient_{{ ifname }}.conf -pf /var/lib/dhcp/dhclient_{{ ifname }}.pid -lf /var/lib/dhcp/dhclient_{{ ifname }}.leases {{ if_metric }} {{ ifname }}"
-
diff --git a/data/templates/dhcp-client/ipv6.override.conf.j2 b/data/templates/dhcp-client/ipv6.override.conf.j2
new file mode 100644
index 000000000..b0c0e0544
--- /dev/null
+++ b/data/templates/dhcp-client/ipv6.override.conf.j2
@@ -0,0 +1,12 @@
+{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %}
+{% set no_release = '-n' if dhcpv6_options.no_release is vyos_defined else '' %}
+{% set dhcp6c_options = '-D -k ' ~ dhcp6_client_dir ~ '/dhcp6c.' ~ ifname ~ '.sock -c ' ~ dhcp6_client_dir ~ '/dhcp6c.' ~ ifname ~ '.conf -p ' ~ dhcp6_client_dir ~ '/dhcp6c.' ~ ifname ~ '.pid ' ~ no_release %}
+
+[Unit]
+ConditionPathExists={{ dhcp6_client_dir }}/dhcp6c.%i.conf
+
+[Service]
+ExecStart=
+ExecStart={{ vrf_command }}/usr/sbin/dhcp6c {{ dhcp6c_options }} {{ ifname }}
+WorkingDirectory={{ dhcp6_client_dir }}
+PIDFile={{ dhcp6_client_dir }}/dhcp6c.%i.pid
diff --git a/data/templates/dhcp-client/override.conf.j2 b/data/templates/dhcp-client/override.conf.j2
new file mode 100644
index 000000000..d09320270
--- /dev/null
+++ b/data/templates/dhcp-client/override.conf.j2
@@ -0,0 +1,15 @@
+### Autogenerated by interface.py ###
+{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %}
+{% set if_metric = '-e IF_METRIC=' ~ dhcp_options.default_route_distance if dhcp_options.default_route_distance is vyos_defined else '' %}
+{% set dhclient_options = '-d -nw -cf ' ~ isc_dhclient_dir ~ '/dhclient_' ~ ifname ~ '.conf -pf ' ~ isc_dhclient_dir ~ '/dhclient_' ~ ifname ~ '.pid -lf ' ~ isc_dhclient_dir ~ '/dhclient_' ~ ifname ~ '.leases ' ~ if_metric %}
+
+[Unit]
+ConditionPathExists={{ isc_dhclient_dir }}/dhclient_%i.conf
+
+[Service]
+ExecStart=
+ExecStart={{ vrf_command }}/sbin/dhclient -4 {{ dhclient_options }} {{ ifname }}
+ExecStop=
+ExecStop={{ vrf_command }}/sbin/dhclient -4 -r {{ dhclient_options }} {{ ifname }}
+WorkingDirectory={{ isc_dhclient_dir }}
+PIDFile={{ isc_dhclient_dir }}/dhclient_%i.pid
diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2
new file mode 100644
index 000000000..3446a9d1b
--- /dev/null
+++ b/data/templates/dns-dynamic/ddclient.conf.j2
@@ -0,0 +1,75 @@
+{% macro render_config(host, address, web_options, ip_suffixes=['']) %}
+{# Address: use=if, if=ethX, usev6=ifv6, ifv6=ethX, usev6=webv6, webv6=https://v6.example.com #}
+{% for ipv in ip_suffixes %}
+use{{ ipv }}={{ address if address == 'web' else 'if' }}{{ ipv }}, \
+{% if address == 'web' %}
+{% if web_options.url is vyos_defined %}
+web{{ ipv }}={{ web_options.url }}, \
+{% endif %}
+{% if web_options.skip is vyos_defined %}
+web-skip{{ ipv }}='{{ web_options.skip }}', \
+{% endif %}
+{% else %}
+if{{ ipv }}={{ address }}, \
+{% endif %}
+{% endfor %}
+{# Other service options #}
+{% for k,v in kwargs.items() %}
+{% if v is vyos_defined %}
+{{ k }}={{ v }}{{ ',' if not loop.last }} \
+{% endif %}
+{% endfor %}
+{# Actual hostname for the service #}
+{{ host }}
+{% endmacro %}
+### Autogenerated by dns_dynamic.py ###
+daemon={{ timeout }}
+syslog=yes
+ssl=yes
+pid={{ config_file | replace('.conf', '.pid') }}
+cache={{ config_file | replace('.conf', '.cache') }}
+{# Explicitly override global options for reliability #}
+web=googledomains {# ddclient default ('dyndns') doesn't support ssl and results in process lockup #}
+use=no {# ddclient default ('ip') results in confusing warning message in log #}
+
+{% if address is vyos_defined %}
+{% for address, service_cfg in address.items() %}
+{% if service_cfg.rfc2136 is vyos_defined %}
+{% for name, config in service_cfg.rfc2136.items() %}
+{% if config.description is vyos_defined %}
+# {{ config.description }}
+
+{% endif %}
+{% for host in config.host_name if config.host_name is vyos_defined %}
+# RFC2136 dynamic DNS configuration for {{ name }}: [{{ config.zone }}, {{ host }}]
+{# Don't append 'new-style' compliant suffix ('usev4', 'usev6', 'ifv4', 'ifv6' etc.)
+ to the properties since 'nsupdate' doesn't support that yet. #}
+{{ render_config(host, address, service_cfg.web_options,
+ protocol='nsupdate', server=config.server, zone=config.zone,
+ password=config.key, ttl=config.ttl) }}
+
+{% endfor %}
+{% endfor %}
+{% endif %}
+{% if service_cfg.service is vyos_defined %}
+{% for name, config in service_cfg.service.items() %}
+{% if config.description is vyos_defined %}
+# {{ config.description }}
+
+{% endif %}
+{% for host in config.host_name if config.host_name is vyos_defined %}
+{% set ip_suffixes = ['v4', 'v6'] if config.ip_version == 'both'
+ else (['v6'] if config.ip_version == 'ipv6' else ['']) %}
+# Web service dynamic DNS configuration for {{ name }}: [{{ config.protocol }}, {{ host }}]
+{# For ipv4 only setup or legacy ipv6 setup, don't append 'new-style' compliant suffix
+ ('usev4', 'ifv4', 'webv4' etc.) to the properties and instead live through the
+ deprecation warnings for better compatibility with most ddclient protocols. #}
+{{ render_config(host, address, service_cfg.web_options, ip_suffixes,
+ protocol=config.protocol, server=config.server, zone=config.zone,
+ login=config.username, password=config.password) }}
+
+{% endfor %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/dns-dynamic/override.conf.j2 b/data/templates/dns-dynamic/override.conf.j2
new file mode 100644
index 000000000..6ca1b8a45
--- /dev/null
+++ b/data/templates/dns-dynamic/override.conf.j2
@@ -0,0 +1,10 @@
+{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %}
+[Unit]
+ConditionPathExists={{ config_file }}
+After=vyos-router.service
+
+[Service]
+PIDFile={{ config_file | replace('.conf', '.pid') }}
+EnvironmentFile=
+ExecStart=
+ExecStart=/usr/bin/ddclient -file {{ config_file }}
diff --git a/data/templates/dynamic-dns/ddclient.conf.j2 b/data/templates/dynamic-dns/ddclient.conf.j2
deleted file mode 100644
index e8ef5ac90..000000000
--- a/data/templates/dynamic-dns/ddclient.conf.j2
+++ /dev/null
@@ -1,53 +0,0 @@
-### Autogenerated by dynamic_dns.py ###
-daemon=1m
-syslog=yes
-ssl=yes
-
-{% if interface is vyos_defined %}
-{% for iface, iface_config in interface.items() %}
-# ddclient configuration for interface "{{ iface }}"
-{% if iface_config.use_web is vyos_defined %}
-{% set web_skip = ", web-skip='" ~ iface_config.use_web.skip ~ "'" if iface_config.use_web.skip is vyos_defined else '' %}
-use=web, web='{{ iface_config.use_web.url }}'{{ web_skip }}
-{% else %}
-{{ 'usev6=ifv6' if iface_config.ipv6_enable is vyos_defined else 'use=if' }}, if={{ iface }}
-{% endif %}
-
-{% if iface_config.rfc2136 is vyos_defined %}
-{% for rfc2136, config in iface_config.rfc2136.items() %}
-{% for dns_record in config.record if config.record is vyos_defined %}
-# RFC2136 dynamic DNS configuration for {{ rfc2136 }}, {{ config.zone }}, {{ dns_record }}
-server={{ config.server }}
-protocol=nsupdate
-password={{ config.key }}
-ttl={{ config.ttl }}
-zone={{ config.zone }}
-{{ dns_record }}
-
-{% endfor %}
-{% endfor %}
-{% endif %}
-
-{% if iface_config.service is vyos_defined %}
-{% for service, config in iface_config.service.items() %}
-{% for dns_record in config.host_name %}
-# DynDNS provider configuration for {{ service }}, {{ dns_record }}
-protocol={{ config.protocol }},
-max-interval=28d,
-{% if config.login is vyos_defined %}
-login={{ config.login }},
-{% endif %}
-password='{{ config.password }}',
-{% if config.server is vyos_defined %}
-server={{ config.server }},
-{% endif %}
-{% if config.zone is vyos_defined %}
-zone={{ config.zone }},
-{% endif %}
-{{ dns_record }}
-
-{% endfor %}
-{% endfor %}
-{% endif %}
-{% endfor %}
-{% endif %}
diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2
index 7a89d29e4..699349e2b 100644
--- a/data/templates/firewall/nftables-policy.j2
+++ b/data/templates/firewall/nftables-policy.j2
@@ -11,7 +11,7 @@ table ip vyos_mangle {
type filter hook prerouting priority -150; policy accept;
{% if route is vyos_defined %}
{% for route_text, conf in route.items() if conf.interface is vyos_defined %}
- iifname { {{ conf.interface | join(",") }} } counter jump VYOS_PBR_{{ route_text }}
+ iifname { {{ conf.interface | join(",") }} } counter jump VYOS_PBR_UD_{{ route_text }}
{% endfor %}
{% endif %}
}
@@ -22,10 +22,10 @@ table ip vyos_mangle {
{% if route is vyos_defined %}
{% for route_text, conf in route.items() %}
- chain VYOS_PBR_{{ route_text }} {
+ chain VYOS_PBR_UD_{{ route_text }} {
{% if conf.rule is vyos_defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
- {{ rule_conf | nft_rule(route_text, rule_id, 'ip') }}
+ {{ rule_conf | nft_rule('route', route_text, rule_id, 'ip') }}
{% endfor %}
{% endif %}
}
@@ -40,7 +40,7 @@ table ip6 vyos_mangle {
type filter hook prerouting priority -150; policy accept;
{% if route6 is vyos_defined %}
{% for route_text, conf in route6.items() if conf.interface is vyos_defined %}
- iifname { {{ ",".join(conf.interface) }} } counter jump VYOS_PBR6_{{ route_text }}
+ iifname { {{ ",".join(conf.interface) }} } counter jump VYOS_PBR6_UD_{{ route_text }}
{% endfor %}
{% endif %}
}
@@ -51,10 +51,10 @@ table ip6 vyos_mangle {
{% if route6 is vyos_defined %}
{% for route_text, conf in route6.items() %}
- chain VYOS_PBR6_{{ route_text }} {
+ chain VYOS_PBR6_UD_{{ route_text }} {
{% if conf.rule is vyos_defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
- {{ rule_conf | nft_rule(route_text, rule_id, 'ip6') }}
+ {{ rule_conf | nft_rule('route6', route_text, rule_id, 'ip6') }}
{% endfor %}
{% endif %}
}
diff --git a/data/templates/firewall/nftables-zone.j2 b/data/templates/firewall/nftables-zone.j2
deleted file mode 100644
index 17ef5101d..000000000
--- a/data/templates/firewall/nftables-zone.j2
+++ /dev/null
@@ -1,78 +0,0 @@
-
-{% macro zone_chains(zone, state_policy=False, ipv6=False) %}
-{% set fw_name = 'ipv6_name' if ipv6 else 'name' %}
-{% set suffix = '6' if ipv6 else '' %}
- chain VYOS_ZONE_FORWARD {
- type filter hook forward priority 1; policy accept;
-{% if state_policy %}
- jump VYOS_STATE_POLICY{{ suffix }}
-{% endif %}
-{% for zone_name, zone_conf in zone.items() %}
-{% if 'local_zone' not in zone_conf %}
- oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }}
-{% endif %}
-{% endfor %}
- }
- chain VYOS_ZONE_LOCAL {
- type filter hook input priority 1; policy accept;
-{% if state_policy %}
- jump VYOS_STATE_POLICY{{ suffix }}
-{% endif %}
-{% for zone_name, zone_conf in zone.items() %}
-{% if 'local_zone' in zone_conf %}
- counter jump VZONE_{{ zone_name }}_IN
-{% endif %}
-{% endfor %}
- }
- chain VYOS_ZONE_OUTPUT {
- type filter hook output priority 1; policy accept;
-{% if state_policy %}
- jump VYOS_STATE_POLICY{{ suffix }}
-{% endif %}
-{% for zone_name, zone_conf in zone.items() %}
-{% if 'local_zone' in zone_conf %}
- counter jump VZONE_{{ zone_name }}_OUT
-{% endif %}
-{% endfor %}
- }
-{% for zone_name, zone_conf in zone.items() %}
-{% if zone_conf.local_zone is vyos_defined %}
- chain VZONE_{{ zone_name }}_IN {
- iifname lo counter return
-{% if zone_conf.from is vyos_defined %}
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
-{% endif %}
- {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
- }
- chain VZONE_{{ zone_name }}_OUT {
- oifname lo counter return
-{% if zone_conf.from_local is vyos_defined %}
-{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
-{% endif %}
- {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
- }
-{% else %}
- chain VZONE_{{ zone_name }} {
- iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }}
-{% if zone_conf.intra_zone_filtering is vyos_defined %}
- iifname { {{ zone_conf.interface | join(",") }} } counter return
-{% endif %}
-{% if zone_conf.from is vyos_defined %}
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
-{% if zone[from_zone].local_zone is not defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endif %}
-{% endfor %}
-{% endif %}
- {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
- }
-{% endif %}
-{% endfor %}
-{% endmacro %}
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 2c7115134..0fbddfaa9 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -1,78 +1,130 @@
#!/usr/sbin/nft -f
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
-{% import 'firewall/nftables-zone.j2' as zone_tmpl %}
+
+flush chain raw FW_CONNTRACK
+flush chain ip6 raw FW_CONNTRACK
+
+table raw {
+ chain FW_CONNTRACK {
+ {{ ipv4_conntrack_action }}
+ }
+}
+
+table ip6 raw {
+ chain FW_CONNTRACK {
+ {{ ipv6_conntrack_action }}
+ }
+}
{% if first_install is not vyos_defined %}
-delete table ip vyos_filter
-{% endif %}
-table ip vyos_filter {
- chain VYOS_FW_FORWARD {
- type filter hook forward priority 0; policy accept;
-{% if state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY
+delete table inet vyos_global_rpfilter
{% endif %}
-{% if interface is vyos_defined %}
-{% for ifname, ifconf in interface.items() %}
-{% if ifconf.in is vyos_defined and ifconf.in.name is vyos_defined %}
- iifname {{ ifname }} counter jump NAME_{{ ifconf.in.name }}
-{% endif %}
-{% if ifconf.out is vyos_defined and ifconf.out.name is vyos_defined %}
- oifname {{ ifname }} counter jump NAME_{{ ifconf.out.name }}
-{% endif %}
-{% endfor %}
+table inet vyos_global_rpfilter {
+ chain PREROUTING {
+ type filter hook prerouting priority -300; policy accept;
+{% if global_options.source_validation is vyos_defined('loose') %}
+ fib saddr oif 0 counter drop
+{% elif global_options.source_validation is vyos_defined('strict') %}
+ fib saddr . iif oif 0 counter drop
{% endif %}
- jump VYOS_POST_FW
}
- chain VYOS_FW_LOCAL {
- type filter hook input priority 0; policy accept;
-{% if state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY
-{% endif %}
-{% if interface is vyos_defined %}
-{% for ifname, ifconf in interface.items() %}
-{% if ifconf.local is vyos_defined and ifconf.local.name is vyos_defined %}
- iifname {{ ifname }} counter jump NAME_{{ ifconf.local.name }}
-{% endif %}
-{% endfor %}
+}
+
+{% if first_install is not vyos_defined %}
+delete table ip vyos_filter
{% endif %}
- jump VYOS_POST_FW
+table ip vyos_filter {
+{% if ipv4 is vyos_defined %}
+{% set ns = namespace(sets=[]) %}
+{% if ipv4.forward is vyos_defined %}
+{% for prior, conf in ipv4.forward.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_FORWARD_{{ prior }} {
+ type filter hook forward priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('FWD', prior, rule_id) }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
- chain VYOS_FW_OUTPUT {
- type filter hook output priority 0; policy accept;
-{% if state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY
-{% endif %}
- jump VYOS_POST_FW
+{% endfor %}
+{% endif %}
+
+{% if ipv4.input is vyos_defined %}
+{% for prior, conf in ipv4.input.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_INPUT_{{ prior }} {
+ type filter hook input priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('INP',prior, rule_id) }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
- chain VYOS_POST_FW {
- return
+{% endfor %}
+{% endif %}
+
+{% if ipv4.output is vyos_defined %}
+{% for prior, conf in ipv4.output.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_OUTPUT_{{ prior }} {
+ type filter hook output priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('OUT', prior, rule_id) }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['OUT_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
+{% endfor %}
+{% endif %}
chain VYOS_FRAG_MARK {
type filter hook prerouting priority -450; policy accept;
ip frag-off & 0x3fff != 0 meta mark set 0xffff1 return
}
-{% if name is vyos_defined %}
-{% set ns = namespace(sets=[]) %}
-{% for name_text, conf in name.items() %}
+{% if ipv4.prerouting is vyos_defined %}
+{% for prior, conf in ipv4.prerouting.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_PREROUTING_{{ prior }} {
+ type filter hook prerouting priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('PRE', prior, rule_id) }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['PRE_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(prior) }}
+ }
+{% endfor %}
+{% endif %}
+
+{% if ipv4.name is vyos_defined %}
+{% for name_text, conf in ipv4.name.items() %}
chain NAME_{{ name_text }} {
-{% if conf.rule is vyos_defined %}
-{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
- {{ rule_conf | nft_rule(name_text, rule_id) }}
-{% if rule_conf.recent is vyos_defined %}
-{% set ns.sets = ns.sets + [name_text + '_' + rule_id] %}
-{% endif %}
-{% endfor %}
-{% endif %}
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('NAM', name_text, rule_id) }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
{{ conf | nft_default_rule(name_text) }}
}
-{% endfor %}
-{% for set_name in ip_fqdn %}
- set FQDN_{{ set_name }} {
- type ipv4_addr
- flags interval
- }
-{% endfor %}
+{% endfor %}
+{% endif %}
+
{% for set_name in ns.sets %}
set RECENT_{{ set_name }} {
type ipv4_addr
@@ -80,6 +132,12 @@ table ip vyos_filter {
flags dynamic
}
{% endfor %}
+{% for set_name in ip_fqdn %}
+ set FQDN_{{ set_name }} {
+ type ipv4_addr
+ flags interval
+ }
+{% endfor %}
{% if geoip_updated.name is vyos_defined %}
{% for setname in geoip_updated.name %}
set {{ setname }} {
@@ -89,99 +147,87 @@ table ip vyos_filter {
{% endfor %}
{% endif %}
{% endif %}
-
{{ group_tmpl.groups(group, False) }}
-
-{% if zone is vyos_defined %}
-{{ zone_tmpl.zone_chains(zone, state_policy is vyos_defined, False) }}
-{% endif %}
-
-{% if state_policy is vyos_defined %}
- chain VYOS_STATE_POLICY {
-{% if state_policy.established is vyos_defined %}
- {{ state_policy.established | nft_state_policy('established') }}
-{% endif %}
-{% if state_policy.invalid is vyos_defined %}
- {{ state_policy.invalid | nft_state_policy('invalid') }}
-{% endif %}
-{% if state_policy.related is vyos_defined %}
- {{ state_policy.related | nft_state_policy('related') }}
-{% endif %}
- return
- }
-{% endif %}
}
{% if first_install is not vyos_defined %}
delete table ip6 vyos_filter
{% endif %}
table ip6 vyos_filter {
- chain VYOS_FW6_FORWARD {
- type filter hook forward priority 0; policy accept;
-{% if state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY6
-{% endif %}
-{% if interface is vyos_defined %}
-{% for ifname, ifconf in interface.items() %}
-{% if ifconf.in is vyos_defined and ifconf.in.ipv6_name is vyos_defined %}
- iifname {{ ifname }} counter jump NAME6_{{ ifconf.in.ipv6_name }}
-{% endif %}
-{% if ifconf.out is vyos_defined and ifconf.out.ipv6_name is vyos_defined %}
- oifname {{ ifname }} counter jump NAME6_{{ ifconf.out.ipv6_name }}
-{% endif %}
-{% endfor %}
-{% endif %}
- jump VYOS_POST_FW6
- }
- chain VYOS_FW6_LOCAL {
- type filter hook input priority 0; policy accept;
-{% if state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY6
-{% endif %}
-{% if interface is vyos_defined %}
-{% for ifname, ifconf in interface.items() %}
-{% if ifconf.local is vyos_defined and ifconf.local.ipv6_name is vyos_defined %}
- iifname {{ ifname }} counter jump NAME6_{{ ifconf.local.ipv6_name }}
-{% endif %}
-{% endfor %}
-{% endif %}
- jump VYOS_POST_FW6
+{% if ipv6 is vyos_defined %}
+{% set ns = namespace(sets=[]) %}
+{% if ipv6.forward is vyos_defined %}
+{% for prior, conf in ipv6.forward.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_IPV6_FORWARD_{{ prior }} {
+ type filter hook forward priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('FWD', prior, rule_id ,'ip6') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
- chain VYOS_FW6_OUTPUT {
- type filter hook output priority 0; policy accept;
-{% if state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY6
-{% endif %}
- jump VYOS_POST_FW6
+{% endfor %}
+{% endif %}
+
+{% if ipv6.input is vyos_defined %}
+{% for prior, conf in ipv6.input.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_IPV6_INPUT_{{ prior }} {
+ type filter hook input priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('INP', prior, rule_id ,'ip6') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
- chain VYOS_POST_FW6 {
- return
+{% endfor %}
+{% endif %}
+
+{% if ipv6.output is vyos_defined %}
+{% for prior, conf in ipv6.output.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_IPV6_OUTPUT_{{ prior }} {
+ type filter hook output priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('OUT', prior, rule_id ,'ip6') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['OUT_ ' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
}
+{% endfor %}
+{% endif %}
+
chain VYOS_FRAG6_MARK {
type filter hook prerouting priority -450; policy accept;
exthdr frag exists meta mark set 0xffff1 return
}
-{% if ipv6_name is vyos_defined %}
-{% set ns = namespace(sets=[]) %}
-{% for name_text, conf in ipv6_name.items() %}
+
+{% if ipv6.name is vyos_defined %}
+{% for name_text, conf in ipv6.name.items() %}
chain NAME6_{{ name_text }} {
-{% if conf.rule is vyos_defined %}
-{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
- {{ rule_conf | nft_rule(name_text, rule_id, 'ip6') }}
-{% if rule_conf.recent is vyos_defined %}
-{% set ns.sets = ns.sets + [name_text + '_' + rule_id] %}
-{% endif %}
-{% endfor %}
-{% endif %}
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'ip6') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
{{ conf | nft_default_rule(name_text, ipv6=True) }}
}
-{% endfor %}
-{% for set_name in ip6_fqdn %}
- set FQDN_{{ set_name }} {
- type ipv6_addr
- flags interval
- }
-{% endfor %}
+{% endfor %}
+{% endif %}
+
{% for set_name in ns.sets %}
set RECENT6_{{ set_name }} {
type ipv6_addr
@@ -189,6 +235,12 @@ table ip6 vyos_filter {
flags dynamic
}
{% endfor %}
+{% for set_name in ip6_fqdn %}
+ set FQDN_{{ set_name }} {
+ type ipv6_addr
+ flags interval
+ }
+{% endfor %}
{% if geoip_updated.ipv6_name is vyos_defined %}
{% for setname in geoip_updated.ipv6_name %}
set {{ setname }} {
@@ -198,25 +250,5 @@ table ip6 vyos_filter {
{% endfor %}
{% endif %}
{% endif %}
-
{{ group_tmpl.groups(group, True) }}
-
-{% if zone is vyos_defined %}
-{{ zone_tmpl.zone_chains(zone, state_policy is vyos_defined, True) }}
-{% endif %}
-
-{% if state_policy is vyos_defined %}
- chain VYOS_STATE_POLICY6 {
-{% if state_policy.established is vyos_defined %}
- {{ state_policy.established | nft_state_policy('established') }}
-{% endif %}
-{% if state_policy.invalid is vyos_defined %}
- {{ state_policy.invalid | nft_state_policy('invalid') }}
-{% endif %}
-{% if state_policy.related is vyos_defined %}
- {{ state_policy.related | nft_state_policy('related') }}
-{% endif %}
- return
- }
-{% endif %}
-}
+} \ No newline at end of file
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index 3e101820c..7fa974254 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -349,6 +349,9 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if afi_config.label.vpn.export is vyos_defined %}
label vpn export {{ afi_config.label.vpn.export }}
{% endif %}
+{% if afi_config.label.vpn.allocation_mode.per_nexthop is vyos_defined %}
+ label vpn export allocation-mode per-nexthop
+{% endif %}
{% if afi_config.local_install is vyos_defined %}
{% for interface in afi_config.local_install.interface %}
local-install {{ interface }}
@@ -481,7 +484,7 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
bgp bestpath compare-routerid
{% 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 }}
+ bgp bestpath med {{ parameters.bestpath.med | join(' ') | replace('_', '-') }}
{% endif %}
{% if parameters.bestpath.peer_type is vyos_defined %}
bgp bestpath peer-type {{ 'multipath-relax' if parameters.bestpath.peer_type.multipath_relax is vyos_defined }}
@@ -565,3 +568,14 @@ bgp route-reflector allow-outbound-policy
timers bgp {{ timers.keepalive }} {{ timers.holdtime }}
{% endif %}
exit
+!
+{% if interface is vyos_defined %}
+{% for iface, iface_config in interface.items() %}
+interface {{ iface }}
+{% if iface_config.mpls.forwarding is vyos_defined %}
+ mpls bgp forwarding
+{% endif %}
+exit
+!
+{% endfor %}
+{% endif %}
diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl
index fdff9772a..3aad8e8dd 100644
--- a/data/templates/frr/daemons.frr.tmpl
+++ b/data/templates/frr/daemons.frr.tmpl
@@ -41,6 +41,7 @@ pimd_options=" --daemon -A 127.0.0.1"
ldpd_options=" --daemon -A 127.0.0.1
{%- if snmp is defined and snmp.ldpd is defined %} -M snmp{% endif -%}
"
+mgmtd_options=" --daemon -A 127.0.0.1"
nhrpd_options=" --daemon -A 127.0.0.1"
eigrpd_options=" --daemon -A 127.0.0.1"
babeld_options=" --daemon -A 127.0.0.1"
diff --git a/data/templates/frr/ospf6d.frr.j2 b/data/templates/frr/ospf6d.frr.j2
index 84394ed1a..b0b5663dd 100644
--- a/data/templates/frr/ospf6d.frr.j2
+++ b/data/templates/frr/ospf6d.frr.j2
@@ -80,6 +80,27 @@ router ospf6 {{ 'vrf ' ~ vrf if vrf is vyos_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 graceful_restart is vyos_defined %}
+{% if graceful_restart.grace_period is vyos_defined %}
+ graceful-restart grace-period {{ graceful_restart.grace_period }}
+{% endif %}
+{% if graceful_restart.helper.enable.router_id is vyos_defined %}
+{% for router_id in graceful_restart.helper.enable.router_id %}
+ graceful-restart helper enable {{ router_id }}
+{% endfor %}
+{% elif graceful_restart.helper.enable is vyos_defined %}
+ graceful-restart helper enable
+{% endif %}
+{% if graceful_restart.helper.planned_only is vyos_defined %}
+ graceful-restart helper planned-only
+{% endif %}
+{% if graceful_restart.helper.lsa_check_disable is vyos_defined %}
+ graceful-restart helper lsa-check-disable
+{% endif %}
+{% if graceful_restart.helper.supported_grace_time is vyos_defined %}
+ graceful-restart helper supported-grace-time {{ graceful_restart.helper.supported_grace_time }}
+{% endif %}
+{% endif %}
{% if log_adjacency_changes is vyos_defined %}
log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is vyos_defined }}
{% endif %}
diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2
index 3f97b7325..040628e82 100644
--- a/data/templates/frr/ospfd.frr.j2
+++ b/data/templates/frr/ospfd.frr.j2
@@ -72,6 +72,9 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% endfor %}
{% endfor %}
{% endif %}
+{% if aggregation.timer is vyos_defined %}
+ aggregation timer {{ aggregation.timer }}
+{% endif %}
{% if area is vyos_defined %}
{% for area_id, area_config in area.items() %}
{% if area_config.area_type is vyos_defined %}
@@ -130,6 +133,9 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if auto_cost.reference_bandwidth is vyos_defined %}
auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }}
{% endif %}
+{% if capability.opaque is vyos_defined %}
+ capability opaque
+{% endif %}
{% 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 %}
@@ -150,6 +156,27 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_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 graceful_restart is vyos_defined %}
+{% if graceful_restart.grace_period is vyos_defined %}
+ graceful-restart grace-period {{ graceful_restart.grace_period }}
+{% endif %}
+{% if graceful_restart.helper.enable.router_id is vyos_defined %}
+{% for router_id in graceful_restart.helper.enable.router_id %}
+ graceful-restart helper enable {{ router_id }}
+{% endfor %}
+{% elif graceful_restart.helper.enable is vyos_defined %}
+ graceful-restart helper enable
+{% endif %}
+{% if graceful_restart.helper.planned_only is vyos_defined %}
+ graceful-restart helper planned-only
+{% endif %}
+{% if graceful_restart.helper.no_strict_lsa_checking is vyos_defined %}
+ no graceful-restart helper strict-lsa-checking
+{% endif %}
+{% if graceful_restart.helper.supported_grace_time is vyos_defined %}
+ graceful-restart helper supported-grace-time {{ graceful_restart.helper.supported_grace_time }}
+{% endif %}
+{% endif %}
{% if log_adjacency_changes is vyos_defined %}
log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is vyos_defined }}
{% endif %}
@@ -200,6 +227,11 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if refresh.timers is vyos_defined %}
refresh timer {{ refresh.timers }}
{% endif %}
+{% if summary_address is vyos_defined %}
+{% for prefix, prefix_options in summary_address.items() %}
+ summary-address {{ prefix }} {{ 'tag ' + prefix_options.tag if prefix_options.tag is vyos_defined }}{{ 'no-advertise' if prefix_options.no_advertise is vyos_defined }}
+{% endfor %}
+{% endif %}
{% if segment_routing is vyos_defined %}
{% if segment_routing.maximum_label_depth is vyos_defined %}
segment-routing node-msd {{ segment_routing.maximum_label_depth }}
diff --git a/data/templates/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2
index 85b89c70c..d54f575b5 100644
--- a/data/templates/high-availability/keepalived.conf.j2
+++ b/data/templates/high-availability/keepalived.conf.j2
@@ -26,6 +26,9 @@ global_defs {
vrrp_garp_master_repeat {{ vrrp.global_parameters.garp.master_repeat }}
{% endif %}
{% endif %}
+{% if vrrp.global_parameters.version is vyos_defined %}
+ vrrp_version {{ vrrp.global_parameters.version }}
+{% endif %}
notify_fifo /run/keepalived/keepalived_notify_fifo
notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
}
@@ -170,7 +173,7 @@ vrrp_sync_group {{ name }} {
{% for vserver, vserver_config in virtual_server.items() %}
# Vserver {{ vserver }}
{% if vserver_config.port is vyos_defined %}
-virtual_server {{ vserver }} {{ vserver_config.port }} {
+virtual_server {{ vserver_config.address }} {{ vserver_config.port }} {
{% else %}
virtual_server fwmark {{ vserver_config.fwmark }} {
{% endif %}
diff --git a/data/templates/ids/fastnetmon.j2 b/data/templates/ids/fastnetmon.j2
index 0340d3c92..f6f03d0db 100644
--- a/data/templates/ids/fastnetmon.j2
+++ b/data/templates/ids/fastnetmon.j2
@@ -29,10 +29,19 @@ unban_only_if_attack_finished = on
# For each subnet, list track speed in bps and pps for both directions
enable_subnet_counters = off
-{% if mode.mirror is vyos_defined %}
+{% if mode is vyos_defined('mirror') %}
mirror_afpacket = on
+{% elif mode is vyos_defined('sflow') %}
+sflow = on
+{% if sflow.port is vyos_defined %}
+sflow_port = {{ sflow.port }}
+{% endif %}
+{% if sflow.listen_address is vyos_defined %}
+sflow_host = {{ sflow.listen_address }}
+{% endif %}
{% endif %}
+
process_incoming_traffic = {{ 'on' if direction is vyos_defined and 'in' in direction else 'off' }}
process_outgoing_traffic = {{ 'on' if direction is vyos_defined and 'out' in direction else 'off' }}
diff --git a/data/templates/ids/fastnetmon_networks_list.j2 b/data/templates/ids/fastnetmon_networks_list.j2
index 5f1b3ba4d..0a0576d2a 100644
--- a/data/templates/ids/fastnetmon_networks_list.j2
+++ b/data/templates/ids/fastnetmon_networks_list.j2
@@ -1,4 +1,4 @@
-{% if network is vyos_defined() %}
+{% if network is vyos_defined %}
{% for net in network %}
{{ net }}
{% endfor %}
diff --git a/data/templates/lldp/vyos.conf.j2 b/data/templates/lldp/vyos.conf.j2
index ec84231d8..dfa422ab8 100644
--- a/data/templates/lldp/vyos.conf.j2
+++ b/data/templates/lldp/vyos.conf.j2
@@ -4,7 +4,7 @@ configure system platform VyOS
configure system description "VyOS {{ version }}"
{% if interface is vyos_defined %}
{% set tmp = [] %}
-{% for iface, iface_options in interface.items() if not iface_options.disable %}
+{% for iface, iface_options in interface.items() if iface_options.disable is not vyos_defined %}
{% if iface == 'all' %}
{% set iface = '*' %}
{% endif %}
diff --git a/data/templates/login/nsswitch.conf.j2 b/data/templates/login/nsswitch.conf.j2
new file mode 100644
index 000000000..65dc88291
--- /dev/null
+++ b/data/templates/login/nsswitch.conf.j2
@@ -0,0 +1,21 @@
+# Automatically generated by system-login.py
+# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+
+passwd: {{ 'mapuid ' if radius is vyos_defined }}{{ 'tacplus ' if tacacs is vyos_defined }}files{{ ' mapname' if radius is vyos_defined }}
+group: {{ 'mapname ' if radius is vyos_defined }}{{ 'tacplus ' if tacacs is vyos_defined }}files
+shadow: files
+gshadow: files
+
+# Per T2678, commenting out myhostname
+hosts: files dns #myhostname
+networks: files
+
+protocols: db files
+services: db files
+ethers: db files
+rpc: db files
+
+netgroup: nis
+
diff --git a/data/templates/login/tacplus_nss.conf.j2 b/data/templates/login/tacplus_nss.conf.j2
new file mode 100644
index 000000000..2a30b1710
--- /dev/null
+++ b/data/templates/login/tacplus_nss.conf.j2
@@ -0,0 +1,74 @@
+#%NSS_TACPLUS-1.0
+# Install this file as /etc/tacplus_nss.conf
+# Edit /etc/nsswitch.conf to add tacplus to the passwd lookup, similar to this
+# where tacplus precede compat (or files), and depending on local policy can
+# follow or precede ldap, nis, etc.
+# passwd: tacplus compat
+#
+# Servers are tried in the order listed, and once a server
+# replies, no other servers are attempted in a given process instantiation
+#
+# This configuration is similar to the libpam_tacplus configuration, but
+# is maintained as a configuration file, since nsswitch.conf doesn't
+# support passing parameters. Parameters must start in the first
+# column, and parsing stops at the first whitespace
+
+# if set, errors and other issues are logged with syslog
+#debug=1
+
+# min_uid is the minimum uid to lookup via tacacs. Setting this to 0
+# means uid 0 (root) is never looked up, good for robustness and performance
+# Cumulus Linux ships with it set to 1001, so we never lookup our standard
+# local users, including the cumulus uid of 1000. Should not be greater
+# than the local tacacs{0..15} uids
+min_uid=900
+
+# This is a comma separated list of usernames that are never sent to
+# a tacacs server, they cause an early not found return.
+#
+# "*" is not a wild card. While it's not a legal username, it turns out
+# that during pathname completion, bash can do an NSS lookup on "*"
+# To avoid server round trip delays, or worse, unreachable server delays
+# on filename completion, we include "*" in the exclusion list.
+exclude_users=root,telegraf,radvd,strongswan,tftp,conservr,frr,ocserv,pdns,_chrony,_lldpd,sshd,openvpn,radius_user,radius_priv_user,*{{ ',' + user | join(',') if user is vyos_defined }}
+
+# The include keyword allows centralizing the tacacs+ server information
+# including the IP address and shared secret
+# include=/etc/tacplus_servers
+
+# The server IP address can be optionally followed by a ':' and a port
+# number (server=1.1.1.1:49). It is strongly recommended that you NOT
+# add secret keys to this file, because it is world readable.
+{% if tacacs.server is vyos_defined %}
+{% for server, server_config in tacacs.server.items() %}
+secret={{ server_config.key }}
+server={{ server }}:{{ server_config.port }}
+
+{% endfor %}
+{% endif %}
+
+{% if tacacs.vrf is vyos_defined %}
+# If the management network is in a vrf, set this variable to the vrf name.
+# This would usually be "mgmt". When this variable is set, the connection to the
+# TACACS+ accounting servers will be made through the named vrf.
+vrf={{ tacacs.vrf }}
+{% endif %}
+
+{% if tacacs.source_address is vyos_defined %}
+# Sets the IPv4 address used as the source IP address when communicating with
+# the TACACS+ server. IPv6 addresses are not supported, nor are hostnames.
+# The address must work when passsed to the bind() system call, that is, it must
+# be valid for the interface being used.
+source_ip={{ tacacs.source_address }}
+{% endif %}
+
+# The connection timeout for an NSS library should be short, since it is
+# invoked for many programs and daemons, and a failure is usually not
+# catastrophic. Not set or set to a negative value disables use of poll().
+# This follows the include of tacplus_servers, so it can override any
+# timeout value set in that file.
+# It's important to have this set in this file, even if the same value
+# as in tacplus_servers, since tacplus_servers should not be readable
+# by users other than root.
+timeout={{ tacacs.timeout }}
+
diff --git a/data/templates/login/tacplus_servers.j2 b/data/templates/login/tacplus_servers.j2
new file mode 100644
index 000000000..5a65d6e68
--- /dev/null
+++ b/data/templates/login/tacplus_servers.j2
@@ -0,0 +1,59 @@
+# Automatically generated by system-login.py
+# TACACS+ configuration file
+
+# This is a common file used by audisp-tacplus, libpam_tacplus, and
+# libtacplus_map config files as shipped.
+#
+# Any tac_plus client config can go here that is common to all users of this
+# file, but typically it's just the TACACS+ server IP address(es) and shared
+# secret(s)
+#
+# This file should normally be mode 600, if you care about the security of your
+# secret key. When set to mode 600 NSS lookups for TACACS users will only work
+# for tacacs users that are logged in, via the local mapping. For root, lookups
+# will work for any tacacs users, logged in or not.
+
+# Set a per-connection timeout of 10 seconds, and enable the use of poll() when
+# trying to read from tacacs servers. Otherwise standard TCP timeouts apply.
+# Not set or set to a negative value disables use of poll(). There are usually
+# multiple connection attempts per login.
+timeout={{ tacacs.timeout }}
+
+{% if tacacs.server is vyos_defined %}
+{% for server, server_config in tacacs.server.items() %}
+secret={{ server_config.key }}
+server={{ server }}:{{ server_config.port }}
+{% endfor %}
+{% endif %}
+
+# If set, login/logout accounting records are sent to all servers in
+# the list, otherwise only to the first responding server
+# Also used by audisp-tacplus per-command accounting, if it sources this file.
+acct_all=1
+
+{% if tacacs.vrf is vyos_defined %}
+# If the management network is in a vrf, set this variable to the vrf name.
+# This would usually be "mgmt". When this variable is set, the connection to the
+# TACACS+ accounting servers will be made through the named vrf.
+vrf={{ tacacs.vrf }}
+{% endif %}
+
+{% if tacacs.source_address is vyos_defined %}
+# Sets the IPv4 address used as the source IP address when communicating with
+# the TACACS+ server. IPv6 addresses are not supported, nor are hostnames.
+# The address must work when passsed to the bind() system call, that is, it must
+# be valid for the interface being used.
+source_ip={{ tacacs.source_address }}
+{% endif %}
+
+# If user_homedir=1, then tacacs users will be set to have a home directory
+# based on their login name, rather than the mapped tacacsN home directory.
+# mkhomedir_helper is used to create the directory if it does not exist (similar
+# to use of pam_mkhomedir.so). This flag is ignored for users with restricted
+# shells, e.g., users mapped to a tacacs privilege level that has enforced
+# per-command authorization (see the tacplus-restrict man page).
+user_homedir=1
+
+service=shell
+protocol=ssh
+
diff --git a/data/templates/mdns-repeater/avahi-daemon.j2 b/data/templates/mdns-repeater/avahi-daemon.j2
index 3aaa7fc82..e0dfd897e 100644
--- a/data/templates/mdns-repeater/avahi-daemon.j2
+++ b/data/templates/mdns-repeater/avahi-daemon.j2
@@ -1,3 +1,4 @@
+### Autogenerated by service_mdns-repeater.py ###
[server]
use-ipv4=yes
use-ipv6=yes
diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2
index 6332ed9c2..f76fbbe79 100644
--- a/data/templates/openvpn/server.conf.j2
+++ b/data/templates/openvpn/server.conf.j2
@@ -48,6 +48,9 @@ push "redirect-gateway def1"
{% if use_lzo_compression is vyos_defined %}
compress lzo
{% endif %}
+{% if offload.dco is not vyos_defined %}
+disable-dco
+{% endif %}
{% if mode is vyos_defined('client') %}
#
@@ -182,7 +185,7 @@ tls-version-min {{ tls.tls_version_min }}
{% endif %}
{% if tls.dh_params is vyos_defined %}
dh /run/openvpn/{{ ifname }}_dh.pem
-{% elif mode is vyos_defined('server') and tls.private_key is vyos_defined %}
+{% else %}
dh none
{% endif %}
{% if tls.auth_key is vyos_defined %}
@@ -197,6 +200,14 @@ tls-client
{% elif tls.role is vyos_defined('passive') %}
tls-server
{% endif %}
+
+{% if tls.peer_fingerprint is vyos_defined %}
+<peer-fingerprint>
+{% for fp in tls.peer_fingerprint %}
+{{ fp }}
+{% endfor %}
+</peer-fingerprint>
+{% endif %}
{% endif %}
# Encryption options
diff --git a/data/templates/pmacct/uacctd.conf.j2 b/data/templates/pmacct/uacctd.conf.j2
index 8fbc09e83..1370f8121 100644
--- a/data/templates/pmacct/uacctd.conf.j2
+++ b/data/templates/pmacct/uacctd.conf.j2
@@ -53,7 +53,7 @@ nfprobe_maxflows[{{ nf_server_key }}]: {{ netflow.max_flows }}
sampling_rate[{{ nf_server_key }}]: {{ netflow.sampling_rate }}
{% endif %}
{% if netflow.source_address is vyos_defined %}
-nfprobe_source_ip[{{ nf_server_key }}]: {{ netflow.source_address }}
+nfprobe_source_ip[{{ nf_server_key }}]: {{ netflow.source_address | bracketize_ipv6 }}
{% endif %}
{% if netflow.timeout is vyos_defined %}
nfprobe_timeouts[{{ nf_server_key }}]: expint={{ netflow.timeout.expiry_interval }}:general={{ netflow.timeout.flow_generic }}:icmp={{ netflow.timeout.icmp }}:maxlife={{ netflow.timeout.max_active_life }}:tcp.fin={{ netflow.timeout.tcp_fin }}:tcp={{ netflow.timeout.tcp_generic }}:tcp.rst={{ netflow.timeout.tcp_rst }}:udp={{ netflow.timeout.udp }}
@@ -73,7 +73,7 @@ sfprobe_agentip[{{ sf_server_key }}]: {{ sflow.agent_address }}
sampling_rate[{{ sf_server_key }}]: {{ sflow.sampling_rate }}
{% endif %}
{% if sflow.source_address is vyos_defined %}
-sfprobe_source_ip[{{ sf_server_key }}]: {{ sflow.source_address }}
+sfprobe_source_ip[{{ sf_server_key }}]: {{ sflow.source_address | bracketize_ipv6 }}
{% endif %}
{% endfor %}
diff --git a/data/templates/rsyslog/logrotate.j2 b/data/templates/rsyslog/logrotate.j2
index 89d1a8a50..cc535c48f 100644
--- a/data/templates/rsyslog/logrotate.j2
+++ b/data/templates/rsyslog/logrotate.j2
@@ -1,4 +1,15 @@
### Autogenerated by system-syslog.py ###
+/var/log/messages {
+ missingok
+ notifempty
+ create
+ rotate 5
+ size=256k
+ postrotate
+ invoke-rc.d rsyslog rotate > /dev/null
+ endscript
+}
+
{% if file is vyos_defined %}
{% for file_name, file_options in file.items() %}
/var/log/user/{{ file_name }} {
diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2
index 5352fc367..dff904129 100644
--- a/data/templates/rsyslog/rsyslog.conf.j2
+++ b/data/templates/rsyslog/rsyslog.conf.j2
@@ -23,11 +23,13 @@ $outchannel global,/var/log/messages,262144,/usr/sbin/logrotate {{ logrotate }}
{% if file is vyos_defined %}
# File based configuration section
{% for file_name, file_options in file.items() %}
-$outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archive.size }},/usr/sbin/logrotate {{ logrotate }}
{% set tmp = [] %}
-{% for facility, facility_options in file_options.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
-{% endfor %}
+$outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archive.size }},/usr/sbin/logrotate {{ logrotate }}
+{% if file_options.facility is vyos_defined %}
+{% for facility, facility_options in file_options.facility.items() %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% endfor %}
+{% endif %}
{{ tmp | join(';') }} :omfile:${{ file }}
{% endfor %}
{% endif %}
@@ -45,9 +47,11 @@ $outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archiv
# Remote logging
{% for host_name, host_options in host.items() %}
{% set tmp = [] %}
-{% for facility, facility_options in host_options.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
-{% endfor %}
+{% if host_options.facility is vyos_defined %}
+{% for facility, facility_options in host_options.facility.items() %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% endfor %}
+{% endif %}
{% if host_options.protocol is vyos_defined('tcp') %}
{% if host_options.format.octet_counted is vyos_defined %}
{{ tmp | join(';') }} @@(o){{ host_name | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format
@@ -63,9 +67,12 @@ $outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archiv
{% if user is defined and user is not none %}
# Log to user terminal
{% for username, user_options in user.items() %}
-{% for facility, facility_options in user_options.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
-{% endfor %}
+{% set tmp = [] %}
+{% if user_options.facility is vyos_defined %}
+{% for facility, facility_options in user_options.facility.items() %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% endfor %}
+{% endif %}
{{ tmp | join(';') }} :omusrmsg:{{ username }}
{% endfor %}
{% endif %}
diff --git a/data/templates/vpp/override.conf.j2 b/data/templates/vpp/override.conf.j2
new file mode 100644
index 000000000..a2c2b04ed
--- /dev/null
+++ b/data/templates/vpp/override.conf.j2
@@ -0,0 +1,14 @@
+[Unit]
+After=
+After=vyos-router.service
+ConditionPathExists=
+ConditionPathExists=/run/vpp/vpp.conf
+
+[Service]
+EnvironmentFile=
+ExecStart=
+ExecStart=/usr/bin/vpp -c /run/vpp/vpp.conf
+WorkingDirectory=
+WorkingDirectory=/run/vpp
+Restart=always
+RestartSec=10
diff --git a/data/templates/vpp/startup.conf.j2 b/data/templates/vpp/startup.conf.j2
new file mode 100644
index 000000000..f33539fba
--- /dev/null
+++ b/data/templates/vpp/startup.conf.j2
@@ -0,0 +1,116 @@
+# Generated by /usr/libexec/vyos/conf_mode/vpp.py
+
+unix {
+ nodaemon
+ log /var/log/vpp.log
+ full-coredump
+ cli-listen /run/vpp/cli.sock
+ gid vpp
+ # exec /etc/vpp/bootstrap.vpp
+{% if unix is vyos_defined %}
+{% if unix.poll_sleep_usec is vyos_defined %}
+ poll-sleep-usec {{ unix.poll_sleep_usec }}
+{% endif %}
+{% endif %}
+}
+
+{% if cpu is vyos_defined %}
+cpu {
+{% if cpu.main_core is vyos_defined %}
+ main-core {{ cpu.main_core }}
+{% endif %}
+{% if cpu.corelist_workers is vyos_defined %}
+ corelist-workers {{ cpu.corelist_workers | join(',') }}
+{% endif %}
+{% if cpu.skip_cores is vyos_defined %}
+ skip-cores {{ cpu.skip_cores }}
+{% endif %}
+{% if cpu.workers is vyos_defined %}
+ workers {{ cpu.workers }}
+{% endif %}
+}
+{% endif %}
+
+{# ip heap-size does not work now (23.06-rc2~1-g3a4e62ad4) #}
+{# vlib_call_all_config_functions: unknown input `ip heap-size 32M ' #}
+{% if ip is vyos_defined %}
+#ip {
+#{% if ip.heap_size is vyos_defined %}
+# heap-size {{ ip.heap_size }}M
+#{% endif %}
+#}
+{% endif %}
+
+{% if ip6 is vyos_defined %}
+ip6 {
+{% if ip6.hash_buckets is vyos_defined %}
+ hash-buckets {{ ip6.hash_buckets }}
+{% endif %}
+{% if ip6.heap_size is vyos_defined %}
+ heap-size {{ ip6.heap_size }}M
+{% endif %}
+}
+{% endif %}
+
+{% if l2learn is vyos_defined %}
+l2learn {
+{% if l2learn.limit is vyos_defined %}
+ limit {{ l2learn.limit }}
+{% endif %}
+}
+{% endif %}
+
+{% if logging is vyos_defined %}
+logging {
+{% if logging.default_log_level is vyos_defined %}
+ default-log-level {{ logging.default_log_level }}
+{% endif %}
+}
+{% endif %}
+
+{% if physmem is vyos_defined %}
+physmem {
+{% if physmem.max_size is vyos_defined %}
+ max-size {{ physmem.max_size.upper() }}
+{% endif %}
+}
+{% endif %}
+
+plugins {
+ path /usr/lib/x86_64-linux-gnu/vpp_plugins/
+ plugin default { disable }
+ plugin dpdk_plugin.so { enable }
+ plugin linux_cp_plugin.so { enable }
+ plugin linux_nl_plugin.so { enable }
+}
+
+linux-cp {
+ lcp-sync
+ lcp-auto-subint
+}
+
+dpdk {
+ # Whitelist the fake PCI address 0000:00:00.0
+ # This prevents all devices from being added to VPP-DPDK by default
+ dev 0000:00:00.0
+{% for iface, iface_config in interface.items() %}
+{% if iface_config.pci is vyos_defined %}
+ dev {{ iface_config.pci }} {
+ name {{ iface }}
+{% if iface_config.num_rx_desc is vyos_defined %}
+ num-rx-desc {{ iface_config.num_rx_desc }}
+{% endif %}
+{% if iface_config.num_tx_desc is vyos_defined %}
+ num-tx-desc {{ iface_config.num_tx_desc }}
+{% endif %}
+{% if iface_config.num_rx_queues is vyos_defined %}
+ num-rx-queues {{ iface_config.num_rx_queues }}
+{% endif %}
+{% if iface_config.num_tx_queues is vyos_defined %}
+ num-tx-queues {{ iface_config.num_tx_queues }}
+{% endif %}
+ }
+{% endif %}
+{% endfor %}
+ uio-bind-force
+}
diff --git a/data/templates/wifi/hostapd.conf.j2 b/data/templates/wifi/hostapd.conf.j2
index f2312d2d4..c3f32da72 100644
--- a/data/templates/wifi/hostapd.conf.j2
+++ b/data/templates/wifi/hostapd.conf.j2
@@ -340,6 +340,11 @@ vht_oper_chwidth={{ capabilities.vht.channel_set_width }}
{% endif %}
{% set output = namespace(value='') %}
+{% if capabilities.vht.channel_set_width is vyos_defined('2') %}
+{% set output.value = output.value ~ '[VHT160]' %}
+{% elif capabilities.vht.channel_set_width is vyos_defined('3') %}
+{% set output.value = output.value ~ '[VHT160-80PLUS80]' %}
+{% endif %}
{% if capabilities.vht.stbc.tx is vyos_defined %}
{% set output.value = output.value ~ '[TX-STBC-2BY1]' %}
{% endif %}
@@ -363,30 +368,21 @@ vht_oper_chwidth={{ capabilities.vht.channel_set_width }}
{% endif %}
{% if capabilities.vht.max_mpdu_exp is vyos_defined %}
{% set output.value = output.value ~ '[MAX-A-MPDU-LEN-EXP-' ~ capabilities.vht.max_mpdu_exp ~ ']' %}
-{% if capabilities.vht.max_mpdu_exp is vyos_defined('2') %}
-{% set output.value = output.value ~ '[VHT160]' %}
-{% endif %}
-{% if capabilities.vht.max_mpdu_exp is vyos_defined('3') %}
-{% set output.value = output.value ~ '[VHT160-80PLUS80]' %}
-{% endif %}
{% endif %}
{% if capabilities.vht.link_adaptation is vyos_defined('unsolicited') %}
{% set output.value = output.value ~ '[VHT-LINK-ADAPT2]' %}
{% elif capabilities.vht.link_adaptation is vyos_defined('both') %}
{% set output.value = output.value ~ '[VHT-LINK-ADAPT3]' %}
{% endif %}
-
{% for short_gi in capabilities.vht.short_gi if capabilities.vht.short_gi is vyos_defined %}
{% set output.value = output.value ~ '[SHORT-GI-' ~ short_gi | upper ~ ']' %}
{% endfor %}
-
{% for beamform in capabilities.vht.beamform if capabilities.vht.beamform is vyos_defined %}
{% set output.value = output.value ~ '[SU-BEAMFORMER]' if beamform is vyos_defined('single-user-beamformer') else '' %}
{% set output.value = output.value ~ '[SU-BEAMFORMEE]' if beamform is vyos_defined('single-user-beamformee') else '' %}
{% set output.value = output.value ~ '[MU-BEAMFORMER]' if beamform is vyos_defined('multi-user-beamformer') else '' %}
{% set output.value = output.value ~ '[MU-BEAMFORMEE]' if beamform is vyos_defined('multi-user-beamformee') else '' %}
{% endfor %}
-
{% if capabilities.vht.antenna_count is vyos_defined and capabilities.vht.antenna_count | int > 1 %}
{% if capabilities.vht.beamform is vyos_defined %}
{% if capabilities.vht.beamform == 'single-user-beamformer' %}
@@ -430,14 +426,22 @@ ieee80211n={{ '1' if 'n' in mode or 'ac' in mode else '0' }}
ignore_broadcast_ssid=1
{% endif %}
-# Station MAC address -based authentication
+{% if type is vyos_defined('access-point') %}
+# Station MAC address-based authentication
# Please note that this kind of access control requires a driver that uses
# hostapd to take care of management frame processing and as such, this can be
# used with driver=hostap or driver=nl80211, but not with driver=atheros.
# 0 = accept unless in deny list
# 1 = deny unless in accept list
# 2 = use external RADIUS server (accept/deny lists are searched first)
-macaddr_acl=0
+macaddr_acl={{ '0' if security.station_address.mode is vyos_defined('accept') else '1' }}
+
+# Accept/deny lists are read from separate files (containing list of
+# MAC addresses, one per line). Use absolute path name to make sure that the
+# files can be read on SIGHUP configuration reloads.
+accept_mac_file={{ hostapd_accept_station_conf }}
+deny_mac_file={{ hostapd_deny_station_conf }}
+{% endif %}
{% if max_stations is vyos_defined %}
# Maximum number of stations allowed in station table. New stations will be
diff --git a/data/templates/wifi/hostapd_accept_station.conf.j2 b/data/templates/wifi/hostapd_accept_station.conf.j2
new file mode 100644
index 000000000..a381c947c
--- /dev/null
+++ b/data/templates/wifi/hostapd_accept_station.conf.j2
@@ -0,0 +1,7 @@
+# List of MAC addresses that are allowed to authenticate (IEEE 802.11)
+# with the AP
+{% if security.station_address.accept.mac is vyos_defined %}
+{% for mac in security.station_address.accept.mac %}
+{{ mac | lower }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/wifi/hostapd_deny_station.conf.j2 b/data/templates/wifi/hostapd_deny_station.conf.j2
new file mode 100644
index 000000000..fb2950dda
--- /dev/null
+++ b/data/templates/wifi/hostapd_deny_station.conf.j2
@@ -0,0 +1,7 @@
+# List of MAC addresses that are not allowed to authenticate
+# (IEEE 802.11) with the access point
+{% if security.station_address.deny.mac is vyos_defined %}
+{% for mac in security.station_address.deny.mac %}
+{{ mac | lower }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/zabbix-agent/10-override.conf.j2 b/data/templates/zabbix-agent/10-override.conf.j2
new file mode 100644
index 000000000..7c296e8fd
--- /dev/null
+++ b/data/templates/zabbix-agent/10-override.conf.j2
@@ -0,0 +1,14 @@
+[Unit]
+After=
+After=vyos-router.service
+ConditionPathExists=
+ConditionPathExists=/run/zabbix/zabbix-agent2.conf
+
+[Service]
+EnvironmentFile=
+ExecStart=
+ExecStart=/usr/sbin/zabbix_agent2 --config /run/zabbix/zabbix-agent2.conf --foreground
+WorkingDirectory=
+WorkingDirectory=/run/zabbix
+Restart=always
+RestartSec=10
diff --git a/data/templates/zabbix-agent/zabbix-agent.conf.j2 b/data/templates/zabbix-agent/zabbix-agent.conf.j2
new file mode 100644
index 000000000..e6dcef872
--- /dev/null
+++ b/data/templates/zabbix-agent/zabbix-agent.conf.j2
@@ -0,0 +1,77 @@
+# Generated by ${vyos_conf_scripts_dir}/service_monitoring_zabbix-agent.py
+
+PidFile=/run/zabbix/zabbix_agent2.pid
+LogFile=/var/log/zabbix/zabbix_agent2.log
+ControlSocket=/run/zabbix/agent.sock
+
+{% if log is vyos_defined %}
+{% if log.size is vyos_defined %}
+### Option: LogFileSize
+# Maximum size of log file in MB.
+# 0 - disable automatic log rotation.
+#
+# Range: 0-1024
+LogFileSize={{ log.size }}
+{% endif %}
+{% if log.remote_commands is vyos_defined %}
+LogRemoteCommands=1
+{% endif %}
+{% if log.debug_level is vyos_defined %}
+{% set mapping = {
+ 'basic': 0,
+ 'critical': 1,
+ 'error': 2,
+ 'warning': 3,
+ 'debug': 4,
+ 'extended-debug': 5
+ } %}
+DebugLevel={{ mapping[log.debug_level] }}
+{% endif %}
+{% endif %}
+
+{% if server is vyos_defined %}
+Server={{ server | bracketize_ipv6 | join(',') }}
+{% endif %}
+{% if server_active is vyos_defined %}
+{% set servers = [] %}
+{% for key, value in server_active.items() %}
+{% if value.port %}
+{% set serv_item = key | bracketize_ipv6 + ':' + value.port %}
+{% set _ = servers.append(serv_item) %}
+{% else %}
+{% set _ = servers.append(key | bracketize_ipv6) %}
+{% endif %}
+{% endfor %}
+ServerActive={{ servers | join(',') }}
+{% endif %}
+
+{% if host_name is vyos_defined %}
+Hostname={{ host_name }}
+{% endif %}
+
+{% if port is vyos_defined %}
+ListenPort={{ port }}
+{% endif %}
+{% if listen_address is vyos_defined %}
+ListenIP={{ listen_address | join(',') }}
+{% endif %}
+
+{% if limits is vyos_defined %}
+{% if limits.buffer_flush_interval is vyos_defined %}
+BufferSend={{ limits.buffer_flush_interval }}
+{% endif %}
+{% if limits.buffer_size is vyos_defined %}
+BufferSize={{ limits.buffer_size }}
+{% endif %}
+{% endif %}
+
+{% if directory is vyos_defined %}
+### Option: Include
+# You may include individual files or all files in a directory in the configuration file.
+Include={{ directory }}/*.conf
+{% endif %}
+
+{% if timeout is vyos_defined %}
+Timeout={{ timeout }}
+{% endif %}
+
diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf
index 11a5bc7bf..41e7627f5 100644
--- a/data/vyos-firewall-init.conf
+++ b/data/vyos-firewall-init.conf
@@ -20,7 +20,7 @@ table raw {
}
chain PREROUTING {
- type filter hook prerouting priority -200; policy accept;
+ type filter hook prerouting priority -300; policy accept;
counter jump VYOS_CT_IGNORE
counter jump VYOS_CT_TIMEOUT
counter jump VYOS_CT_PREROUTING_HOOK
@@ -29,7 +29,7 @@ table raw {
}
chain OUTPUT {
- type filter hook output priority -200; policy accept;
+ type filter hook output priority -300; policy accept;
counter jump VYOS_CT_IGNORE
counter jump VYOS_CT_TIMEOUT
counter jump VYOS_CT_OUTPUT_HOOK
@@ -73,7 +73,7 @@ table raw {
}
chain FW_CONNTRACK {
- accept
+ return
}
}
@@ -82,6 +82,10 @@ table ip6 raw {
type filter hook forward priority -300; policy accept;
}
+ chain vyos_rpfilter {
+ type filter hook prerouting priority -300; policy accept;
+ }
+
chain PREROUTING {
type filter hook prerouting priority -300; policy accept;
counter jump VYOS_CT_PREROUTING_HOOK
@@ -105,6 +109,6 @@ table ip6 raw {
}
chain FW_CONNTRACK {
- accept
+ return
}
}
diff --git a/debian/control b/debian/control
index ec08968c9..ee45a5fe3 100644
--- a/debian/control
+++ b/debian/control
@@ -6,14 +6,8 @@ Build-Depends:
debhelper (>= 9),
dh-python,
fakeroot,
- gcc-multilib [amd64],
- clang [amd64],
+ gcc,
iproute2,
- llvm [amd64],
- libbpf-dev [amd64],
- libelf-dev (>= 0.2) [amd64],
- libpcap-dev [amd64],
- build-essential,
libvyosconfig0 (>= 0.0.7),
libzmq3-dev,
python3 (>= 3.10),
@@ -32,6 +26,10 @@ Standards-Version: 3.9.6
Package: vyos-1x
Architecture: amd64 arm64
+Pre-Depends:
+ libnss-tacplus [amd64],
+ libpam-tacplus [amd64],
+ libpam-radius-auth [amd64]
Depends:
${python3:Depends} (>= 3.10),
aardvark-dns,
@@ -74,6 +72,7 @@ Depends:
iperf,
iperf3,
iproute2 (>= 6.0.0),
+ iptables,
iputils-arping,
isc-dhcp-client,
isc-dhcp-relay,
@@ -84,17 +83,17 @@ Depends:
lcdproc-extra-drivers,
libatomic1,
libauparse0,
- libbpf1 [amd64],
libcharon-extra-plugins (>=5.9),
libcharon-extauth-plugins (>=5.9),
libndp-tools,
libnetfilter-conntrack3,
libnfnetlink0,
- libpam-radius-auth (>= 1.5.0),
libqmi-utils,
libstrongswan-extra-plugins (>=5.9),
libstrongswan-standard-plugins (>=5.9),
+ libvppinfra [amd64],
libvyosconfig0,
+ linux-cpupower,
lldpd,
lm-sensors,
lsscsi,
@@ -146,6 +145,7 @@ Depends:
python3-tabulate,
python3-vici (>= 5.7.2),
python3-voluptuous,
+ python3-vpp-api [amd64],
python3-xmltodict,
python3-zmq,
qrencode,
@@ -180,6 +180,9 @@ Depends:
uidmap,
usb-modeswitch,
usbutils,
+ vpp [amd64],
+ vpp-plugin-core [amd64],
+ vpp-plugin-dpdk [amd64],
vyatta-bash,
vyatta-cfg,
vyos-http-api-tools,
@@ -188,6 +191,7 @@ Depends:
wireguard-tools,
wireless-regdb,
wpasupplicant (>= 0.6.7),
+ zabbix-agent2,
ndppd,
miniupnpd-nftables
Description: VyOS configuration scripts and data
diff --git a/debian/rules b/debian/rules
index 55e02fae6..e6bbeeafb 100755
--- a/debian/rules
+++ b/debian/rules
@@ -28,17 +28,10 @@ override_dh_gencontrol:
override_dh_auto_build:
make all
-ifeq ($(DEB_TARGET_ARCH),amd64)
- # Only build XDP on amd64 systems
- make vyxdp
-endif
override_dh_auto_install:
dh_auto_install
- # convert the XML to dictionaries
- env PYTHONPATH=python python3 python/vyos/xml/generate.py
-
cd python; python3 setup.py install --install-layout=deb --root ../$(DIR); cd ..
# Install scripts
@@ -55,6 +48,10 @@ override_dh_auto_install:
mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/op_mode
cp -r src/op_mode/* $(DIR)/$(VYOS_LIBEXEC_DIR)/op_mode
+ # Install op mode scripts
+ mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/init
+ cp -r src/init/* $(DIR)/$(VYOS_LIBEXEC_DIR)/init
+
# Install validators
mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/validators
cp -r src/validators/* $(DIR)/$(VYOS_LIBEXEC_DIR)/validators
@@ -128,9 +125,6 @@ override_dh_auto_install:
mkdir -p $(DIR)/usr/lib/udev
cp src/helpers/vyos_net_name $(DIR)/usr/lib/udev
-ifeq ($(DEB_TARGET_ARCH),amd64)
- # We only install XDP on amd64 systems
- mkdir -p $(DIR)/$(VYOS_DATA_DIR)/xdp
- cp -r src/xdp/xdp_prog_kern.o $(DIR)/$(VYOS_DATA_DIR)/xdp
- find src/xdp -perm /a+x -exec cp {} $(DIR)/$(VYOS_SBIN_DIR) \;
-endif
+override_dh_installsystemd:
+ dh_installsystemd -pvyos-1x --name vyos-router vyos-router.service
+ dh_installsystemd -pvyos-1x --name vyos vyos.target
diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index 07cdf8c74..9e43669be 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -30,6 +30,7 @@ usr/bin/vyos-hostsd-client
usr/lib
usr/libexec/vyos/completion
usr/libexec/vyos/conf_mode
+usr/libexec/vyos/init
usr/libexec/vyos/op_mode
usr/libexec/vyos/services
usr/libexec/vyos/system
diff --git a/debian/vyos-1x.links b/debian/vyos-1x.links
new file mode 100644
index 000000000..0e2d1b841
--- /dev/null
+++ b/debian/vyos-1x.links
@@ -0,0 +1 @@
+/etc/netplug/linkup.d/vyos-python-helper /etc/netplug/linkdown.d/vyos-python-helper
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 6653cd585..b43416152 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -1,4 +1,4 @@
-#!/bin/sh -e
+#!/bin/bash
# Turn off Debian default for %sudo
sed -i -e '/^%sudo/d' /etc/sudoers || true
@@ -29,10 +29,66 @@ do
sed -i "/^# Standard Un\*x authentication\./i${PAM_CONFIG}" $file
done
+# We do not make use of a TACACS UNIX group - drop it
+if grep -q '^tacacs' /etc/group; then
+ delgroup tacacs
+fi
+
+# Both RADIUS and TACACS users belong to aaa group - this must be added first
+if ! grep -q '^aaa' /etc/group; then
+ addgroup --firstgid 1000 --quiet aaa
+fi
+
+# Remove TACACS user added by base package - we use our own UID range and group
+# assignments - see below
+if grep -q '^tacacs' /etc/passwd; then
+ if [ $(id -u tacacs0) -ge 1000 ]; then
+ level=0
+ vyos_group=vyattaop
+ while [ $level -lt 16 ]; do
+ userdel tacacs${level} || true
+ rm -rf /home/tacacs${level} || true
+ level=$(( level+1 ))
+ done 2>&1
+ fi
+fi
+
+# Add TACACS system users required for TACACS based system authentication
+if ! grep -q '^tacacs' /etc/passwd; then
+ # Add the tacacs group and all 16 possible tacacs privilege-level users to
+ # the password file, home directories, etc. The accounts are not enabled
+ # for local login, since they are only used to provide uid/gid/homedir for
+ # the mapped TACACS+ logins (and lookups against them). The tacacs15 user
+ # is also added to the sudo group, and vyattacfg group rather than vyattaop
+ # (used for tacacs0-14).
+ level=0
+ vyos_group=vyattaop
+ while [ $level -lt 16 ]; do
+ adduser --quiet --system --firstuid 900 --disabled-login --ingroup users \
+ --no-create-home --gecos "TACACS+ mapped user at privilege level ${level}" \
+ --shell /bin/vbash tacacs${level}
+ adduser --quiet tacacs${level} frrvty
+ adduser --quiet tacacs${level} adm
+ adduser --quiet tacacs${level} dip
+ adduser --quiet tacacs${level} users
+ adduser --quiet tacacs${level} aaa
+ if [ $level -lt 15 ]; then
+ adduser --quiet tacacs${level} vyattaop
+ adduser --quiet tacacs${level} operator
+ else
+ adduser --quiet tacacs${level} vyattacfg
+ adduser --quiet tacacs${level} sudo
+ adduser --quiet tacacs${level} disk
+ adduser --quiet tacacs${level} frr
+ fi
+ level=$(( level+1 ))
+ done 2>&1 | grep -v 'User tacacs${level} already exists'
+fi
+
# Add RADIUS operator user for RADIUS authenticated users to map to
if ! grep -q '^radius_user' /etc/passwd; then
- adduser --quiet --firstuid 1000 --disabled-login --ingroup vyattaop \
- --no-create-home --gecos "radius user" \
+ adduser --quiet --firstuid 1000 --disabled-login --ingroup users \
+ --no-create-home --gecos "RADIUS mapped user at privilege level operator" \
--shell /sbin/radius_shell radius_user
adduser --quiet radius_user frrvty
adduser --quiet radius_user vyattaop
@@ -40,12 +96,13 @@ if ! grep -q '^radius_user' /etc/passwd; then
adduser --quiet radius_user adm
adduser --quiet radius_user dip
adduser --quiet radius_user users
+ adduser --quiet radius_user aaa
fi
# Add RADIUS admin user for RADIUS authenticated users to map to
if ! grep -q '^radius_priv_user' /etc/passwd; then
- adduser --quiet --firstuid 1000 --disabled-login --ingroup vyattacfg \
- --no-create-home --gecos "radius privileged user" \
+ adduser --quiet --firstuid 1000 --disabled-login --ingroup users \
+ --no-create-home --gecos "RADIUS mapped user at privilege level admin" \
--shell /sbin/radius_shell radius_priv_user
adduser --quiet radius_priv_user frrvty
adduser --quiet radius_priv_user vyattacfg
@@ -55,6 +112,7 @@ if ! grep -q '^radius_priv_user' /etc/passwd; then
adduser --quiet radius_priv_user disk
adduser --quiet radius_priv_user users
adduser --quiet radius_priv_user frr
+ adduser --quiet radius_priv_user aaa
fi
# add hostsd group for vyos-hostsd
@@ -68,9 +126,24 @@ if ! grep -q '^dhcpd' /etc/passwd; then
adduser --quiet dhcpd hostsd
fi
-# ensure hte proxy user has a proper shell
+# ensure the proxy user has a proper shell
chsh -s /bin/sh proxy
+# create /opt/vyatta/etc/config/scripts/vyos-preconfig-bootup.script
+PRECONFIG_SCRIPT=/opt/vyatta/etc/config/scripts/vyos-preconfig-bootup.script
+if [ ! -x $PRECONFIG_SCRIPT ]; then
+ mkdir -p $(dirname $PRECONFIG_SCRIPT)
+ touch $PRECONFIG_SCRIPT
+ chmod 755 $PRECONFIG_SCRIPT
+ cat <<EOF >>$PRECONFIG_SCRIPT
+#!/bin/sh
+# This script is executed at boot time before VyOS configuration is applied.
+# Any modifications required to work around unfixed bugs or use
+# services not available through the VyOS CLI system can be placed here.
+
+EOF
+fi
+
# create /opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script
POSTCONFIG_SCRIPT=/opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script
if [ ! -x $POSTCONFIG_SCRIPT ]; then
@@ -127,3 +200,18 @@ systemctl enable vyos-config-cloud-init.service
# Generate API GraphQL schema
/usr/libexec/vyos/services/api/graphql/generate/generate_schema.py
+
+# Update XML cache
+python3 /usr/lib/python3/dist-packages/vyos/xml_ref/update_cache.py
+
+# T1797: disable VPP support for rolling release, should be used by developers
+# only (in the initial phase). If you wan't to enable VPP use the below command
+# on your VyOS installation:
+#
+# sudo mv /opt/vyatta/share/vyatta-cfg/vpp /opt/vyatta/share/vyatta-cfg/templates/vpp
+if [ -d /opt/vyatta/share/vyatta-cfg/templates/vpp ]; then
+ if [ -d /opt/vyatta/share/vyatta-cfg/vpp ]; then
+ rm -rf /opt/vyatta/share/vyatta-cfg/vpp
+ fi
+ mv /opt/vyatta/share/vyatta-cfg/templates/vpp /opt/vyatta/share/vyatta-cfg/vpp
+fi
diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst
index 949ffcbc4..e355ffa84 100644
--- a/debian/vyos-1x.preinst
+++ b/debian/vyos-1x.preinst
@@ -3,6 +3,10 @@ dpkg-divert --package vyos-1x --add --no-rename /etc/security/capability.conf
dpkg-divert --package vyos-1x --add --no-rename /lib/systemd/system/lcdproc.service
dpkg-divert --package vyos-1x --add --no-rename /etc/logrotate.d/conntrackd
dpkg-divert --package vyos-1x --add --no-rename /usr/share/pam-configs/radius
+dpkg-divert --package vyos-1x --add --no-rename /usr/share/pam-configs/tacplus
dpkg-divert --package vyos-1x --add --no-rename /etc/rsyslog.conf
dpkg-divert --package vyos-1x --add --no-rename /etc/skel/.bashrc
dpkg-divert --package vyos-1x --add --no-rename /etc/skel/.profile
+dpkg-divert --package vyos-1x --add --no-rename /etc/sysctl.d/80-vpp.conf
+dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplugd.conf
+dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplug
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index 6651fc642..6b712a70f 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -3,7 +3,7 @@
<node name="container" owner="${vyos_conf_scripts_dir}/container.py">
<properties>
<help>Container applications</help>
- <priority>1280</priority>
+ <priority>450</priority>
</properties>
<children>
<tagNode name="name">
@@ -110,7 +110,7 @@
<constraint>
<regex>[ !#-%&amp;(-~]+</regex>
</constraint>
- <constraintErrorMessage>Entrypoint must be ascii characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
+ <constraintErrorMessage>Entrypoint must be ASCII characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="host-name">
@@ -133,7 +133,7 @@
<constraint>
<regex>[ !#-%&amp;(-~]+</regex>
</constraint>
- <constraintErrorMessage>Command must be ascii characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
+ <constraintErrorMessage>Command must be ASCII characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="arguments">
@@ -142,9 +142,29 @@
<constraint>
<regex>[ !#-%&amp;(-~]+</regex>
</constraint>
- <constraintErrorMessage>The command's arguments must be ascii characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
+ <constraintErrorMessage>The command's arguments must be ASCII characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
</properties>
</leafNode>
+ <tagNode name="label">
+ <properties>
+ <help>Add label variables</help>
+ <constraint>
+ <regex>[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?</regex>
+ </constraint>
+ <constraintErrorMessage>Label variable name must be alphanumeric and can contain hyphen, dots and underscores</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Set label option value</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Set label option value</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
<leafNode name="memory">
<properties>
<help>Memory (RAM) available to this container</help>
@@ -213,6 +233,7 @@
<help>Publish port to the container</help>
</properties>
<children>
+ #include <include/listen-address.xml.i>
<leafNode name="source">
<properties>
<help>Source host port</help>
@@ -334,6 +355,42 @@
</properties>
<defaultValue>rw</defaultValue>
</leafNode>
+ <leafNode name="propagation">
+ <properties>
+ <help>Volume bind propagation</help>
+ <completionHelp>
+ <list>shared slave private rshared rslave rprivate</list>
+ </completionHelp>
+ <valueHelp>
+ <format>shared</format>
+ <description>Sub-mounts of the original mount are exposed to replica mounts</description>
+ </valueHelp>
+ <valueHelp>
+ <format>slave</format>
+ <description>Allow replica mount to see sub-mount from the original mount but not vice versa</description>
+ </valueHelp>
+ <valueHelp>
+ <format>private</format>
+ <description>Sub-mounts within a mount are not visible to replica mounts or the original mount</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rshared</format>
+ <description>Allows sharing of mount points and their nested mount points between both the original and replica mounts</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rslave</format>
+ <description>Allows mount point and their nested mount points between original an replica mounts</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rprivate</format>
+ <description>No mount points within original or replica mounts in any direction</description>
+ </valueHelp>
+ <constraint>
+ <regex>(shared|slave|private|rshared|rslave|rprivate)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>rprivate</defaultValue>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in
index 2a2597dd5..42715c9bb 100644
--- a/interface-definitions/dhcp-relay.xml.in
+++ b/interface-definitions/dhcp-relay.xml.in
@@ -9,6 +9,7 @@
<priority>910</priority>
</properties>
<children>
+ #include <include/generic-disable-node.xml.i>
#include <include/generic-interface-multi-broadcast.xml.i>
<leafNode name="listen-interface">
<properties>
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index 1830cc1ad..583de7ba9 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -129,7 +129,7 @@
<properties>
<help>Bootstrap file name</help>
<constraint>
- <regex>[-_a-zA-Z0-9./]+</regex>
+ <regex>[[:ascii:]]{1,253}</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/dhcpv6-relay.xml.in b/interface-definitions/dhcpv6-relay.xml.in
index 947adef75..a80317609 100644
--- a/interface-definitions/dhcpv6-relay.xml.in
+++ b/interface-definitions/dhcpv6-relay.xml.in
@@ -9,6 +9,7 @@
<priority>900</priority>
</properties>
<children>
+ #include <include/generic-disable-node.xml.i>
<tagNode name="listen-interface">
<properties>
<help>Interface for DHCPv6 Relay Agent to listen for requests</help>
diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in
index 48c101d73..a0720f3aa 100644
--- a/interface-definitions/dns-dynamic.xml.in
+++ b/interface-definitions/dns-dynamic.xml.in
@@ -4,149 +4,102 @@
<children>
<node name="dns">
<properties>
- <help>Domain Name System related services</help>
+ <help>Domain Name System (DNS) related services</help>
</properties>
<children>
- <node name="dynamic" owner="${vyos_conf_scripts_dir}/dynamic_dns.py">
+ <node name="dynamic" owner="${vyos_conf_scripts_dir}/dns_dynamic.py">
<properties>
<help>Dynamic DNS</help>
</properties>
<children>
- <tagNode name="interface">
+ <tagNode name="address">
<properties>
- <help>Interface to send Dynamic DNS updates for</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
+ <help>Obtain IP address to send Dynamic DNS update for</help>
<valueHelp>
<format>txt</format>
- <description>Interface name</description>
+ <description>Use interface to obtain the IP address</description>
</valueHelp>
+ <valueHelp>
+ <format>web</format>
+ <description>Use HTTP(S) web request to obtain the IP address</description>
+ </valueHelp>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ <list>web</list>
+ </completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.i>
+ <regex>web</regex>
</constraint>
</properties>
<children>
- <tagNode name="rfc2136">
+ <node name="web-options">
<properties>
- <help>RFC2136 Update name</help>
+ <help>Options when using HTTP(S) web request to obtain the IP address</help>
</properties>
<children>
- <leafNode name="key">
+ #include <include/url.xml.i>
+ <leafNode name="skip">
<properties>
- <help>File containing the secret key shared with remote DNS server</help>
+ <help>Pattern to skip from the HTTP(S) respose</help>
<valueHelp>
- <format>filename</format>
- <description>File in /config/auth directory</description>
+ <format>txt</format>
+ <description>Pattern to skip from the HTTP(S) respose to extract the external IP address</description>
</valueHelp>
</properties>
</leafNode>
- <leafNode name="record">
- <properties>
- <help>Record to be updated</help>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="server">
- <properties>
- <help>Server to be updated</help>
- </properties>
- </leafNode>
- <leafNode name="ttl">
+ </children>
+ </node>
+ <tagNode name="rfc2136">
+ <properties>
+ <help>RFC2136 nsupdate configuration</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>RFC2136 nsupdate service name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/dns/dynamic-service-host-name-server.xml.i>
+ <leafNode name="key">
<properties>
- <help>Time To Live (default: 600)</help>
+ <help>File containing the TSIG secret key shared with remote DNS server</help>
<valueHelp>
- <format>u32:1-86400</format>
- <description>DNS forwarding cache size</description>
+ <format>filename</format>
+ <description>File in /config/auth directory</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-86400"/>
+ <validator name="file-path" argument="--strict --parent-dir /config/auth"/>
</constraint>
</properties>
- <defaultValue>600</defaultValue>
</leafNode>
+ #include <include/dns/time-to-live.xml.i>
<leafNode name="zone">
<properties>
- <help>Zone to be updated</help>
+ <help>Forwarding zone to be updated</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>RFC2136 Zone to be updated</description>
+ </valueHelp>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
</properties>
</leafNode>
</children>
</tagNode>
<tagNode name="service">
<properties>
- <help>Service being used for Dynamic DNS</help>
- <completionHelp>
- <list>afraid changeip cloudflare dnspark dslreports dyndns easydns namecheap noip sitelutions zoneedit</list>
- </completionHelp>
+ <help>Dynamic DNS configuration</help>
<valueHelp>
<format>txt</format>
- <description>Dynanmic DNS service with a custom name</description>
- </valueHelp>
- <valueHelp>
- <format>afraid</format>
- <description>afraid.org Services</description>
- </valueHelp>
- <valueHelp>
- <format>changeip</format>
- <description>changeip.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>cloudflare</format>
- <description>cloudflare.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>dnspark</format>
- <description>dnspark.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>dslreports</format>
- <description>dslreports.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>dyndns</format>
- <description>dyndns.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>easydns</format>
- <description>easydns.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>namecheap</format>
- <description>namecheap.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>noip</format>
- <description>noip.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>sitelutions</format>
- <description>sitelutions.com Services</description>
- </valueHelp>
- <valueHelp>
- <format>zoneedit</format>
- <description>zoneedit.com Services</description>
+ <description>Dynamic DNS service name</description>
</valueHelp>
- <constraint>
- <regex>(custom|afraid|changeip|cloudflare|dnspark|dslreports|dyndns|easydns|namecheap|noip|sitelutions|zoneedit|\w+)</regex>
- </constraint>
- <constraintErrorMessage>You can use only predefined list of services or word characters (_, a-z, A-Z, 0-9) as service name</constraintErrorMessage>
</properties>
<children>
- <leafNode name="host-name">
- <properties>
- <help>Hostname to register with Dynamic DNS service</help>
- <constraint>
- #include <include/constraint/host-name.xml.i>
- </constraint>
- <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="login">
- <properties>
- <help>Login/Username for Dynamic DNS service</help>
- </properties>
- </leafNode>
+ #include <include/generic-description.xml.i>
+ #include <include/dns/dynamic-service-host-name-server.xml.i>
+ #include <include/generic-username.xml.i>
#include <include/generic-password.xml.i>
<leafNode name="protocol">
<properties>
@@ -159,7 +112,6 @@
</constraint>
</properties>
</leafNode>
- #include <include/server-ipv4-fqdn.xml.i>
<leafNode name="zone">
<properties>
<help>DNS zone to update (not used by all protocols)</help>
@@ -169,33 +121,49 @@
</valueHelp>
</properties>
</leafNode>
- </children>
- </tagNode>
- <node name="use-web">
- <properties>
- <help>Use HTTP(S) web request to obtain external IP address instead of the IP address associated with the interface</help>
- </properties>
- <children>
- <leafNode name="skip">
+ <leafNode name="ip-version">
<properties>
- <help>Pattern to skip from the respose</help>
+ <help>IP address version to use</help>
<valueHelp>
- <format>txt</format>
- <description>Pattern to skip from the respose of the given URL to extract the external IP address</description>
+ <format>_ipv4</format>
+ <description>Use only IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>_ipv6</format>
+ <description>Use only IPv6 address</description>
</valueHelp>
+ <valueHelp>
+ <format>both</format>
+ <description>Use both IPv4 and IPv6 address</description>
+ </valueHelp>
+ <completionHelp>
+ <list>ipv4 ipv6 both</list>
+ </completionHelp>
+ <constraint>
+ <regex>(ipv[46]|both)</regex>
+ </constraint>
+ <constraintErrorMessage>IP Version must be literal 'ipv4', 'ipv6' or 'both'</constraintErrorMessage>
</properties>
+ <defaultValue>ipv4</defaultValue>
</leafNode>
- #include <include/url.xml.i>
</children>
- </node>
- <leafNode name="ipv6-enable">
- <properties>
- <help>Explicitly use IPv6 address instead of IPv4 address to update the Dynamic DNS IP address</help>
- <valueless/>
- </properties>
- </leafNode>
+ </tagNode>
</children>
</tagNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Time in seconds to wait between DNS updates</help>
+ <valueHelp>
+ <format>u32:60-3600</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 60-3600"/>
+ </constraint>
+ <constraintErrorMessage>Timeout must be between 60 and 3600 seconds</constraintErrorMessage>
+ </properties>
+ <defaultValue>300</defaultValue>
+ </leafNode>
</children>
</node>
</children>
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index ced1c9c31..86dc47a47 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -5,7 +5,7 @@
<children>
<node name="dns">
<properties>
- <help>Domain Name System related services</help>
+ <help>Domain Name System (DNS) related services</help>
</properties>
<children>
<node name="forwarding" owner="${vyos_conf_scripts_dir}/dns_forwarding.py">
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 69901e5d3..127f4b7e7 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -6,66 +6,7 @@
<help>Firewall</help>
</properties>
<children>
- <leafNode name="all-ping">
- <properties>
- <help>Policy for handling of all IPv4 ICMP echo requests</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable processing of all IPv4 ICMP echo requests</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable processing of all IPv4 ICMP echo requests</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>enable</defaultValue>
- </leafNode>
- <leafNode name="broadcast-ping">
- <properties>
- <help>Policy for handling broadcast IPv4 ICMP echo and timestamp requests</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable processing of broadcast IPv4 ICMP echo/timestamp requests</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable processing of broadcast IPv4 ICMP echo/timestamp requests</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <leafNode name="config-trap">
- <properties>
- <help>SNMP trap generation on firewall configuration changes</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable sending SNMP trap on firewall configuration change</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable sending SNMP trap on firewall configuration change</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
+ #include <include/firewall/global-options.xml.i>
<node name="group">
<properties>
<help>Firewall group</help>
@@ -343,640 +284,28 @@
</tagNode>
</children>
</node>
- <tagNode name="interface">
+ <node name="ipv4">
<properties>
- <help>Interface name to apply firewall configuration</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
- <constraint>
- #include <include/constraint/interface-name-with-wildcard.xml.i>
- </constraint>
+ <help>IPv4 firewall</help>
</properties>
<children>
- <node name="in">
- <properties>
- <help>Forwarded packets on inbound interface</help>
- </properties>
- <children>
- #include <include/firewall/name.xml.i>
- </children>
- </node>
- <node name="out">
- <properties>
- <help>Forwarded packets on outbound interface</help>
- </properties>
- <children>
- #include <include/firewall/name.xml.i>
- </children>
- </node>
- <node name="local">
- <properties>
- <help>Packets destined for this router</help>
- </properties>
- <children>
- #include <include/firewall/name.xml.i>
- </children>
- </node>
- </children>
- </tagNode>
- <leafNode name="ip-src-route">
- <properties>
- <help>Policy for handling IPv4 packets with source route option</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable processing of IPv4 packets with source route option</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable processing of IPv4 packets with source route option</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <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/default-action.xml.i>
- #include <include/firewall/enable-default-log.xml.i>
- #include <include/generic-description.xml.i>
- <leafNode name="default-jump-target">
- <properties>
- <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <tagNode name="rule">
- <properties>
- <help>Firewall rule number (IPv6)</help>
- <valueHelp>
- <format>u32:1-999999</format>
- <description>Number for this Firewall rule</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-999999"/>
- </constraint>
- <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
- </properties>
- <children>
- #include <include/firewall/action.xml.i>
- #include <include/generic-description.xml.i>
- <node name="destination">
- <properties>
- <help>Destination parameters</help>
- </properties>
- <children>
- #include <include/firewall/address-ipv6.xml.i>
- #include <include/firewall/fqdn.xml.i>
- #include <include/firewall/geoip.xml.i>
- #include <include/firewall/source-destination-group-ipv6.xml.i>
- #include <include/firewall/port.xml.i>
- #include <include/firewall/address-mask-ipv6.xml.i>
- </children>
- </node>
- <node name="source">
- <properties>
- <help>Source parameters</help>
- </properties>
- <children>
- #include <include/firewall/address-ipv6.xml.i>
- #include <include/firewall/fqdn.xml.i>
- #include <include/firewall/geoip.xml.i>
- #include <include/firewall/source-destination-group-ipv6.xml.i>
- #include <include/firewall/port.xml.i>
- #include <include/firewall/address-mask-ipv6.xml.i>
- </children>
- </node>
- #include <include/firewall/common-rule.xml.i>
- #include <include/firewall/dscp.xml.i>
- #include <include/firewall/packet-options.xml.i>
- #include <include/firewall/hop-limit.xml.i>
- #include <include/firewall/connection-mark.xml.i>
- <node name="icmpv6">
- <properties>
- <help>ICMPv6 type and code information</help>
- </properties>
- <children>
- <leafNode name="code">
- <properties>
- <help>ICMPv6 code</help>
- <valueHelp>
- <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</help>
- <valueHelp>
- <format>u32:0-255</format>
- <description>ICMPv6 type (0-255)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-255"/>
- </constraint>
- </properties>
- </leafNode>
- #include <include/firewall/icmpv6-type-name.xml.i>
- </children>
- </node>
- <leafNode name="jump-target">
- <properties>
- <help>Set jump target. Action jump must be defined to use this setting</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- #include <include/firewall/nft-queue.xml.i>
- </children>
- </tagNode>
- </children>
- </tagNode>
- <leafNode name="ipv6-receive-redirects">
- <properties>
- <help>Policy for handling received ICMPv6 redirect messages</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable processing of received ICMPv6 redirect messages</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable processing of received ICMPv6 redirect messages</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <leafNode name="ipv6-src-route">
- <properties>
- <help>Policy for handling IPv6 packets with routing extension header</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable processing of IPv6 packets with routing header type 2</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable processing of IPv6 packets with routing header</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <leafNode name="log-martians">
- <properties>
- <help>Policy for logging IPv4 packets with invalid addresses</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable logging of IPv4 packets with invalid addresses</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable logging of Ipv4 packets with invalid addresses</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>enable</defaultValue>
- </leafNode>
- <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/default-action.xml.i>
- #include <include/firewall/enable-default-log.xml.i>
- #include <include/generic-description.xml.i>
- <leafNode name="default-jump-target">
- <properties>
- <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <tagNode name="rule">
- <properties>
- <help>Firewall rule number (IPv4)</help>
- <valueHelp>
- <format>u32:1-999999</format>
- <description>Number for this Firewall rule</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-999999"/>
- </constraint>
- <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
- </properties>
- <children>
- #include <include/firewall/action.xml.i>
- #include <include/generic-description.xml.i>
- <node name="destination">
- <properties>
- <help>Destination parameters</help>
- </properties>
- <children>
- #include <include/firewall/address.xml.i>
- #include <include/firewall/fqdn.xml.i>
- #include <include/firewall/geoip.xml.i>
- #include <include/firewall/source-destination-group.xml.i>
- #include <include/firewall/port.xml.i>
- #include <include/firewall/address-mask.xml.i>
- </children>
- </node>
- <node name="source">
- <properties>
- <help>Source parameters</help>
- </properties>
- <children>
- #include <include/firewall/address.xml.i>
- #include <include/firewall/fqdn.xml.i>
- #include <include/firewall/geoip.xml.i>
- #include <include/firewall/source-destination-group.xml.i>
- #include <include/firewall/port.xml.i>
- #include <include/firewall/address-mask.xml.i>
- </children>
- </node>
- #include <include/firewall/common-rule.xml.i>
- #include <include/firewall/dscp.xml.i>
- #include <include/firewall/packet-options.xml.i>
- #include <include/firewall/connection-mark.xml.i>
- <node name="icmp">
- <properties>
- <help>ICMP type and code information</help>
- </properties>
- <children>
- <leafNode name="code">
- <properties>
- <help>ICMP code</help>
- <valueHelp>
- <format>u32:0-255</format>
- <description>ICMP code (0-255)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-255"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="type">
- <properties>
- <help>ICMP type</help>
- <valueHelp>
- <format>u32:0-255</format>
- <description>ICMP type (0-255)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-255"/>
- </constraint>
- </properties>
- </leafNode>
- #include <include/firewall/icmp-type-name.xml.i>
- </children>
- </node>
- <leafNode name="jump-target">
- <properties>
- <help>Set jump target. Action jump must be defined to use this setting</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- #include <include/firewall/ttl.xml.i>
- #include <include/firewall/nft-queue.xml.i>
- </children>
- </tagNode>
- </children>
- </tagNode>
- <leafNode name="receive-redirects">
- <properties>
- <help>Policy for handling received IPv4 ICMP redirect messages</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable processing of received IPv4 ICMP redirect messages</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable processing of received IPv4 ICMP redirect messages</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <leafNode name="resolver-cache">
- <properties>
- <help>Retains last successful value if domain resolution fails</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="resolver-interval">
- <properties>
- <help>Domain resolver update interval</help>
- <valueHelp>
- <format>u32:10-3600</format>
- <description>Interval (seconds)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 10-3600"/>
- </constraint>
- </properties>
- <defaultValue>300</defaultValue>
- </leafNode>
- <leafNode name="send-redirects">
- <properties>
- <help>Policy for sending IPv4 ICMP redirect messages</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable sending IPv4 ICMP redirect messages</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable sending IPv4 ICMP redirect messages</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>enable</defaultValue>
- </leafNode>
- <leafNode name="source-validation">
- <properties>
- <help>Policy for source validation by reversed path, as specified in RFC3704</help>
- <completionHelp>
- <list>strict loose disable</list>
- </completionHelp>
- <valueHelp>
- <format>strict</format>
- <description>Enable Strict Reverse Path Forwarding as defined in RFC3704</description>
- </valueHelp>
- <valueHelp>
- <format>loose</format>
- <description>Enable Loose Reverse Path Forwarding as defined in RFC3704</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>No source validation</description>
- </valueHelp>
- <constraint>
- <regex>(strict|loose|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <node name="state-policy">
- <properties>
- <help>Global firewall state-policy</help>
- </properties>
- <children>
- <node name="established">
- <properties>
- <help>Global firewall policy for packets part of an established connection</help>
- </properties>
- <children>
- #include <include/firewall/action-accept-drop-reject.xml.i>
- #include <include/firewall/log.xml.i>
- #include <include/firewall/rule-log-level.xml.i>
- </children>
- </node>
- <node name="invalid">
- <properties>
- <help>Global firewall policy for packets part of an invalid connection</help>
- </properties>
- <children>
- #include <include/firewall/action-accept-drop-reject.xml.i>
- #include <include/firewall/log.xml.i>
- #include <include/firewall/rule-log-level.xml.i>
- </children>
- </node>
- <node name="related">
- <properties>
- <help>Global firewall policy for packets part of a related connection</help>
- </properties>
- <children>
- #include <include/firewall/action-accept-drop-reject.xml.i>
- #include <include/firewall/log.xml.i>
- #include <include/firewall/rule-log-level.xml.i>
- </children>
- </node>
+ #include <include/firewall/ipv4-hook-forward.xml.i>
+ #include <include/firewall/ipv4-hook-input.xml.i>
+ #include <include/firewall/ipv4-hook-output.xml.i>
+ #include <include/firewall/ipv4-custom-name.xml.i>
</children>
</node>
- <leafNode name="syn-cookies">
- <properties>
- <help>Policy for using TCP SYN cookies with IPv4</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable use of TCP SYN cookies with IPv4</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable use of TCP SYN cookies with IPv4</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>enable</defaultValue>
- </leafNode>
- <leafNode name="twa-hazards-protection">
+ <node name="ipv6">
<properties>
- <help>RFC1337 TCP TIME-WAIT assasination hazards protection</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable RFC1337 TIME-WAIT hazards protection</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable RFC1337 TIME-WAIT hazards protection</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
- </properties>
- <defaultValue>disable</defaultValue>
- </leafNode>
- <tagNode name="zone">
- <properties>
- <help>Zone-policy</help>
- <valueHelp>
- <format>txt</format>
- <description>Zone name</description>
- </valueHelp>
- <constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
- </constraint>
+ <help>IPv6 firewall</help>
</properties>
<children>
- #include <include/generic-description.xml.i>
- #include <include/firewall/enable-default-log.xml.i>
- <leafNode name="default-action">
- <properties>
- <help>Default-action for traffic coming into this zone</help>
- <completionHelp>
- <list>drop reject</list>
- </completionHelp>
- <valueHelp>
- <format>drop</format>
- <description>Drop silently</description>
- </valueHelp>
- <valueHelp>
- <format>reject</format>
- <description>Drop and notify source</description>
- </valueHelp>
- <constraint>
- <regex>(drop|reject)</regex>
- </constraint>
- </properties>
- <defaultValue>drop</defaultValue>
- </leafNode>
- <tagNode name="from">
- <properties>
- <help>Zone from which to filter traffic</help>
- <completionHelp>
- <path>zone-policy zone</path>
- </completionHelp>
- </properties>
- <children>
- <node name="firewall">
- <properties>
- <help>Firewall options</help>
- </properties>
- <children>
- <leafNode name="ipv6-name">
- <properties>
- <help>IPv6 firewall ruleset</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="name">
- <properties>
- <help>IPv4 firewall ruleset</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
- <leafNode name="interface">
- <properties>
- <help>Interface associated with zone</help>
- <valueHelp>
- <format>txt</format>
- <description>Interface associated with zone</description>
- </valueHelp>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
- <multi/>
- </properties>
- </leafNode>
- <node name="intra-zone-filtering">
- <properties>
- <help>Intra-zone filtering</help>
- </properties>
- <children>
- <leafNode name="action">
- <properties>
- <help>Action for intra-zone traffic</help>
- <completionHelp>
- <list>accept drop</list>
- </completionHelp>
- <valueHelp>
- <format>accept</format>
- <description>Accept traffic</description>
- </valueHelp>
- <valueHelp>
- <format>drop</format>
- <description>Drop silently</description>
- </valueHelp>
- <constraint>
- <regex>(accept|drop)</regex>
- </constraint>
- </properties>
- </leafNode>
- <node name="firewall">
- <properties>
- <help>Use the specified firewall chain</help>
- </properties>
- <children>
- <leafNode name="ipv6-name">
- <properties>
- <help>IPv6 firewall ruleset</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="name">
- <properties>
- <help>IPv4 firewall ruleset</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
- <leafNode name="local-zone">
- <properties>
- <help>Zone to be local-zone</help>
- <valueless/>
- </properties>
- </leafNode>
+ #include <include/firewall/ipv6-hook-forward.xml.i>
+ #include <include/firewall/ipv6-hook-input.xml.i>
+ #include <include/firewall/ipv6-hook-output.xml.i>
+ #include <include/firewall/ipv6-custom-name.xml.i>
</children>
- </tagNode>
+ </node>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index c5e46ed38..4f55916fa 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -30,6 +30,22 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="version">
+ <properties>
+ <help>Default VRRP version to use, IPv6 always uses VRRP version 3</help>
+ <valueHelp>
+ <format>2</format>
+ <description>VRRP version 2</description>
+ </valueHelp>
+ <valueHelp>
+ <format>3</format>
+ <description>VRRP version 3</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-3"/>
+ </constraint>
+ </properties>
+ </leafNode>
</children>
</node>
<tagNode name="group">
@@ -320,9 +336,10 @@
</node>
<tagNode name="virtual-server">
<properties>
- <help>Load-balancing virtual server address</help>
+ <help>Load-balancing virtual server alias</help>
</properties>
<children>
+ #include <include/address-ipv4-ipv6-single.xml.i>
<leafNode name="algorithm">
<properties>
<help>Schedule algorithm (default - least-connection)</help>
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
index e5918b765..b93ba67d8 100644
--- 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
@@ -8,8 +8,9 @@
</valueHelp>
<constraint>
<validator name="ipv4-prefix"/>
+ <validator name="ipv4-host"/>
</constraint>
- <constraintErrorMessage>Not a valid CIDR formatted prefix</constraintErrorMessage>
+ <constraintErrorMessage>Not a valid IP address or prefix</constraintErrorMessage>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/address-ipv4-ipv6-single.xml.i b/interface-definitions/include/address-ipv4-ipv6-single.xml.i
new file mode 100644
index 000000000..dc3d6fc1b
--- /dev/null
+++ b/interface-definitions/include/address-ipv4-ipv6-single.xml.i
@@ -0,0 +1,18 @@
+<!-- include start from interface/address-ipv4-ipv6.xml.i -->
+<leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/afi-export-import.xml.i b/interface-definitions/include/bgp/afi-export-import.xml.i
index 86817cdb3..5223af0ae 100644
--- a/interface-definitions/include/bgp/afi-export-import.xml.i
+++ b/interface-definitions/include/bgp/afi-export-import.xml.i
@@ -32,6 +32,7 @@
</valueHelp>
<completionHelp>
<path>vrf name</path>
+ <list>default</list>
</completionHelp>
<multi/>
</properties>
diff --git a/interface-definitions/include/bgp/afi-label.xml.i b/interface-definitions/include/bgp/afi-label.xml.i
index 9535d19e8..2c5eed18b 100644
--- a/interface-definitions/include/bgp/afi-label.xml.i
+++ b/interface-definitions/include/bgp/afi-label.xml.i
@@ -29,6 +29,19 @@
</constraint>
</properties>
</leafNode>
+ <node name="allocation-mode">
+ <properties>
+ <help>Label allocation mode</help>
+ </properties>
+ <children>
+ <leafNode name="per-nexthop">
+ <properties>
+ <help>Allocate a label per connected next-hop in the VRF</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/interface-definitions/include/bgp/afi-vpn-label.xml.i b/interface-definitions/include/bgp/afi-vpn-label.xml.i
new file mode 100644
index 000000000..6c7e73d9b
--- /dev/null
+++ b/interface-definitions/include/bgp/afi-vpn-label.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from bgp/afi-vpn-label.xml.i -->
+<leafNode name="label">
+ <properties>
+ <help>MPLS label value assigned to route</help>
+ <valueHelp>
+ <format>u32:0-1048575</format>
+ <description>MPLS label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-1048575"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index d69fd7dab..504385b53 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -355,15 +355,7 @@
<help>Apply local policy routing to interface</help>
</properties>
<children>
- <leafNode name="interface">
- <properties>
- <help>Interface</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/generic-interface-multi.xml.i>
</children>
</node>
</children>
@@ -386,18 +378,7 @@
</properties>
<children>
#include <include/bgp/route-distinguisher.xml.i>
- <leafNode name="label">
- <properties>
- <help>MPLS label value assigned to route</help>
- <valueHelp>
- <format>u32:0-1048575</format>
- <description>MPLS label value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-1048575"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/bgp/afi-vpn-label.xml.i>
</children>
</tagNode>
</children>
@@ -766,18 +747,7 @@
</properties>
<children>
#include <include/bgp/route-distinguisher.xml.i>
- <leafNode name="label">
- <properties>
- <help>MPLS label value assigned to route</help>
- <valueHelp>
- <format>u32:0-1048575</format>
- <description>MPLS label value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-1048575"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/bgp/afi-vpn-label.xml.i>
</children>
</tagNode>
</children>
@@ -840,12 +810,7 @@
<help>Specify handling for BUM packets</help>
</properties>
<children>
- <leafNode name="disable">
- <properties>
- <help>Do not flood any BUM packets</help>
- <valueless/>
- </properties>
- </leafNode>
+ #include <include/generic-disable-node.xml.i>
<leafNode name="head-end-replication">
<properties>
<help>Flood BUM packets using head-end replication</help>
@@ -873,6 +838,36 @@
</node>
</children>
</node>
+<tagNode name="interface">
+ <properties>
+ <help>Configure interface related parameters, e.g. MPLS</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/interface-name.xml.i>
+ </constraint>
+ </properties>
+ <children>
+ <node name="mpls">
+ <properties>
+ <help>MPLS options</help>
+ </properties>
+ <children>
+ <leafNode name="forwarding">
+ <properties>
+ <help>Enable MPLS forwarding for eBGP directly connected peers</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</tagNode>
<node name="listen">
<properties>
<help>Listen for and accept BGP dynamic neighbors from range</help>
@@ -1128,25 +1123,26 @@
<valueless/>
</properties>
</leafNode>
- <node name="med">
+ <leafNode name="med">
<properties>
<help>MED attribute comparison parameters</help>
+ <completionHelp>
+ <list>confed missing-as-worst</list>
+ </completionHelp>
+ <valueHelp>
+ <format>confed</format>
+ <description>Compare MEDs among confederation paths</description>
+ </valueHelp>
+ <valueHelp>
+ <format>missing-as-worst</format>
+ <description>Treat missing route as a MED as the least preferred one</description>
+ </valueHelp>
+ <constraint>
+ <regex>(confed|missing-as-worst)</regex>
+ </constraint>
+ <multi/>
</properties>
- <children>
- <leafNode name="confed">
- <properties>
- <help>Compare MEDs among confederation paths</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="missing-as-worst">
- <properties>
- <help>Treat missing route as a MED as the least preferred one</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
+ </leafNode>
<node name="peer-type">
<properties>
<help>Peer type</help>
@@ -1545,7 +1541,9 @@
</properties>
<children>
#include <include/bgp/neighbor-afi-ipv4-unicast.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-vpn.xml.i>
#include <include/bgp/neighbor-afi-ipv6-unicast.xml.i>
+ #include <include/bgp/neighbor-afi-ipv6-vpn.xml.i>
#include <include/bgp/neighbor-afi-l2vpn-evpn.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i b/interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i
new file mode 100644
index 000000000..6a39041a3
--- /dev/null
+++ b/interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i
@@ -0,0 +1,4 @@
+<!-- include start from constraint/interface-name-with-wildcard-and-inverted.xml.i -->
+<regex>(\!?)(bond|br|dum|en|ersp|eth|gnv|ifb|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|veth|vti|vtun|vxlan|wg|wlan|wwan)([0-9]?)(\*?)(.+)?|(\!?)lo</regex>
+<validator name="file-path --lookup-path /sys/class/net --directory"/>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i b/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
new file mode 100644
index 000000000..ee1af2a36
--- /dev/null
+++ b/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
@@ -0,0 +1,34 @@
+<!-- include start from dns/dynamic-service-host-name-server.xml.i -->
+<leafNode name="host-name">
+ <properties>
+ <help>Hostname to register with Dynamic DNS service</help>
+ <constraint>
+ #include <include/constraint/host-name.xml.i>
+ </constraint>
+ <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage>
+ <multi/>
+ </properties>
+</leafNode>
+<leafNode name="server">
+ <properties>
+ <help>Remote Dynamic DNS server to send updates to</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of the remote server</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of the remote server</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>Fully qualified domain name of the remote server</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ <validator name="fqdn"/>
+ </constraint>
+ <constraintErrorMessage>Remote server must be IP address or fully qualified domain name</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/action-and-notrack.xml.i b/interface-definitions/include/firewall/action-and-notrack.xml.i
new file mode 100644
index 000000000..5f81a1451
--- /dev/null
+++ b/interface-definitions/include/firewall/action-and-notrack.xml.i
@@ -0,0 +1,41 @@
+<!-- include start from firewall/action-and-notrack.xml.i -->
+<leafNode name="action">
+ <properties>
+ <help>Rule action</help>
+ <completionHelp>
+ <list>accept jump notrack reject return drop queue</list>
+ </completionHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>jump</format>
+ <description>Jump to another chain</description>
+ </valueHelp>
+ <valueHelp>
+ <format>reject</format>
+ <description>Reject matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>return</format>
+ <description>Return from the current chain and continue at the next rule of the last chain</description>
+ </valueHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>queue</format>
+ <description>Enqueue packet to userspace</description>
+ </valueHelp>
+ <valueHelp>
+ <format>notrack</format>
+ <description>Igone connection tracking</description>
+ </valueHelp>
+ <constraint>
+ <regex>(accept|jump|notrack|reject|return|drop|queue)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i
new file mode 100644
index 000000000..7a2eb86d4
--- /dev/null
+++ b/interface-definitions/include/firewall/common-rule-inet.xml.i
@@ -0,0 +1,374 @@
+<!-- include start from firewall/common-rule-inet.xml.i -->
+#include <include/firewall/action.xml.i>
+#include <include/generic-description.xml.i>
+#include <include/firewall/dscp.xml.i>
+#include <include/firewall/packet-options.xml.i>
+#include <include/firewall/connection-mark.xml.i>
+#include <include/firewall/nft-queue.xml.i>
+<leafNode name="disable">
+ <properties>
+ <help>Option to disable firewall rule</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<node name="fragment">
+ <properties>
+ <help>IP fragment match</help>
+ </properties>
+ <children>
+ <leafNode name="match-frag">
+ <properties>
+ <help>Second and further fragments of fragmented packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="match-non-frag">
+ <properties>
+ <help>Head fragments or unfragmented packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="ipsec">
+ <properties>
+ <help>Inbound IPsec packets</help>
+ </properties>
+ <children>
+ <leafNode name="match-ipsec">
+ <properties>
+ <help>Inbound IPsec packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="match-none">
+ <properties>
+ <help>Inbound non-IPsec packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="limit">
+ <properties>
+ <help>Rate limit using a token bucket filter</help>
+ </properties>
+ <children>
+ <leafNode name="burst">
+ <properties>
+ <help>Maximum number of packets to allow in excess of rate</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Maximum number of packets to allow in excess of rate</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="rate">
+ <properties>
+ <help>Maximum average matching rate</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>integer/unit (Example: 5/minute)</description>
+ </valueHelp>
+ <constraint>
+ <regex>\d+/(second|minute|hour|day)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<leafNode name="log">
+ <properties>
+ <help>Option to log packets matching rule</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable log</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable log</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="log">
+ <properties>
+ <help>Option to log packets matching rule</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable log</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable log</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+#include <include/firewall/rule-log-options.xml.i>
+<node name="connection-status">
+ <properties>
+ <help>Connection status</help>
+ </properties>
+ <children>
+ <leafNode name="nat">
+ <properties>
+ <help>NAT connection status</help>
+ <completionHelp>
+ <list>destination source</list>
+ </completionHelp>
+ <valueHelp>
+ <format>destination</format>
+ <description>Match connections that are subject to destination NAT</description>
+ </valueHelp>
+ <valueHelp>
+ <format>source</format>
+ <description>Match connections that are subject to source NAT</description>
+ </valueHelp>
+ <constraint>
+ <regex>(destination|source)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<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="recent">
+ <properties>
+ <help>Parameters for matching recently seen sources</help>
+ </properties>
+ <children>
+ <leafNode name="count">
+ <properties>
+ <help>Source addresses seen more than N times</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Source addresses seen more than N times</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="time">
+ <properties>
+ <help>Source addresses seen in the last second/minute/hour</help>
+ <completionHelp>
+ <list>second minute hour</list>
+ </completionHelp>
+ <valueHelp>
+ <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>
+ <regex>(second|minute|hour)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="state">
+ <properties>
+ <help>Session state</help>
+ </properties>
+ <children>
+ <leafNode name="established">
+ <properties>
+ <help>Established state</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="invalid">
+ <properties>
+ <help>Invalid state</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="new">
+ <properties>
+ <help>New state</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="related">
+ <properties>
+ <help>Related state</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+#include <include/firewall/tcp-flags.xml.i>
+<node name="time">
+ <properties>
+ <help>Time to match rule</help>
+ </properties>
+ <children>
+ <leafNode name="startdate">
+ <properties>
+ <help>Date to start matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter date using following notation - YYYY-MM-DD</description>
+ </valueHelp>
+ <constraint>
+ <regex>(\d{4}\-\d{2}\-\d{2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="starttime">
+ <properties>
+ <help>Time of day to start matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter time using using 24 hour notation - hh:mm:ss</description>
+ </valueHelp>
+ <constraint>
+ <regex>([0-2][0-9](\:[0-5][0-9]){1,2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="stopdate">
+ <properties>
+ <help>Date to stop matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter date using following notation - YYYY-MM-DD</description>
+ </valueHelp>
+ <constraint>
+ <regex>(\d{4}\-\d{2}\-\d{2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="stoptime">
+ <properties>
+ <help>Time of day to stop matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter time using using 24 hour notation - hh:mm:ss</description>
+ </valueHelp>
+ <constraint>
+ <regex>([0-2][0-9](\:[0-5][0-9]){1,2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="weekdays">
+ <properties>
+ <help>Comma separated weekdays to match rule on</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of day (Monday, Tuesday, Wednesday, Thursdays, Friday, Saturday, Sunday)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:0-6</format>
+ <description>Day number (0 = Sunday ... 6 = Saturday)</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i
new file mode 100644
index 000000000..a1071a09a
--- /dev/null
+++ b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i
@@ -0,0 +1,331 @@
+<!-- include start from firewall/common-rule-ipv4-raw.xml.i -->
+#include <include/firewall/action-and-notrack.xml.i>
+#include <include/generic-description.xml.i>
+#include <include/firewall/dscp.xml.i>
+#include <include/firewall/ttl.xml.i>
+#include <include/firewall/nft-queue.xml.i>
+<node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address.xml.i>
+ #include <include/firewall/address-mask.xml.i>
+ #include <include/firewall/fqdn.xml.i>
+ #include <include/firewall/geoip.xml.i>
+ #include <include/firewall/mac-address.xml.i>
+ #include <include/firewall/port.xml.i>
+ #include <include/firewall/source-destination-group.xml.i>
+ </children>
+</node>
+<leafNode name="disable">
+ <properties>
+ <help>Option to disable firewall rule</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<node name="fragment">
+ <properties>
+ <help>IP fragment match</help>
+ </properties>
+ <children>
+ <leafNode name="match-frag">
+ <properties>
+ <help>Second and further fragments of fragmented packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="match-non-frag">
+ <properties>
+ <help>Head fragments or unfragmented packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="icmp">
+ <properties>
+ <help>ICMP type and code information</help>
+ </properties>
+ <children>
+ <leafNode name="code">
+ <properties>
+ <help>ICMP code</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>ICMP code (0-255)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>ICMP type</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>ICMP type (0-255)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/firewall/icmp-type-name.xml.i>
+ </children>
+</node>
+<node name="ipsec">
+ <properties>
+ <help>Inbound IPsec packets</help>
+ </properties>
+ <children>
+ <leafNode name="match-ipsec">
+ <properties>
+ <help>Inbound IPsec packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="match-none">
+ <properties>
+ <help>Inbound non-IPsec packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="limit">
+ <properties>
+ <help>Rate limit using a token bucket filter</help>
+ </properties>
+ <children>
+ <leafNode name="burst">
+ <properties>
+ <help>Maximum number of packets to allow in excess of rate</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Maximum number of packets to allow in excess of rate</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="rate">
+ <properties>
+ <help>Maximum average matching rate</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>integer/unit (Example: 5/minute)</description>
+ </valueHelp>
+ <constraint>
+ <regex>\d+/(second|minute|hour|day)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<leafNode name="log">
+ <properties>
+ <help>Option to log packets matching rule</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable log</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable log</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+#include <include/firewall/rule-log-options.xml.i>
+<node name="connection-status">
+ <properties>
+ <help>Connection status</help>
+ </properties>
+ <children>
+ <leafNode name="nat">
+ <properties>
+ <help>NAT connection status</help>
+ <completionHelp>
+ <list>destination source</list>
+ </completionHelp>
+ <valueHelp>
+ <format>destination</format>
+ <description>Match connections that are subject to destination NAT</description>
+ </valueHelp>
+ <valueHelp>
+ <format>source</format>
+ <description>Match connections that are subject to source NAT</description>
+ </valueHelp>
+ <constraint>
+ <regex>(destination|source)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<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="recent">
+ <properties>
+ <help>Parameters for matching recently seen sources</help>
+ </properties>
+ <children>
+ <leafNode name="count">
+ <properties>
+ <help>Source addresses seen more than N times</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Source addresses seen more than N times</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="time">
+ <properties>
+ <help>Source addresses seen in the last second/minute/hour</help>
+ <completionHelp>
+ <list>second minute hour</list>
+ </completionHelp>
+ <valueHelp>
+ <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>
+ <regex>(second|minute|hour)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address.xml.i>
+ #include <include/firewall/address-mask.xml.i>
+ #include <include/firewall/fqdn.xml.i>
+ #include <include/firewall/geoip.xml.i>
+ #include <include/firewall/mac-address.xml.i>
+ #include <include/firewall/port.xml.i>
+ #include <include/firewall/source-destination-group.xml.i>
+ </children>
+</node>
+#include <include/firewall/tcp-flags.xml.i>
+<node name="time">
+ <properties>
+ <help>Time to match rule</help>
+ </properties>
+ <children>
+ <leafNode name="startdate">
+ <properties>
+ <help>Date to start matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter date using following notation - YYYY-MM-DD</description>
+ </valueHelp>
+ <constraint>
+ <regex>(\d{4}\-\d{2}\-\d{2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="starttime">
+ <properties>
+ <help>Time of day to start matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter time using using 24 hour notation - hh:mm:ss</description>
+ </valueHelp>
+ <constraint>
+ <regex>([0-2][0-9](\:[0-5][0-9]){1,2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="stopdate">
+ <properties>
+ <help>Date to stop matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter date using following notation - YYYY-MM-DD</description>
+ </valueHelp>
+ <constraint>
+ <regex>(\d{4}\-\d{2}\-\d{2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="stoptime">
+ <properties>
+ <help>Time of day to stop matching rule</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Enter time using using 24 hour notation - hh:mm:ss</description>
+ </valueHelp>
+ <constraint>
+ <regex>([0-2][0-9](\:[0-5][0-9]){1,2})</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="weekdays">
+ <properties>
+ <help>Comma separated weekdays to match rule on</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of day (Monday, Tuesday, Wednesday, Thursdays, Friday, Saturday, Sunday)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:0-6</format>
+ <description>Day number (0 = Sunday ... 6 = Saturday)</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/common-rule-ipv4.xml.i b/interface-definitions/include/firewall/common-rule-ipv4.xml.i
new file mode 100644
index 000000000..4ed179ae7
--- /dev/null
+++ b/interface-definitions/include/firewall/common-rule-ipv4.xml.i
@@ -0,0 +1,72 @@
+<!-- include start from firewall/common-rule-ipv4.xml.i -->
+#include <include/firewall/common-rule-inet.xml.i>
+#include <include/firewall/ttl.xml.i>
+<node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address.xml.i>
+ #include <include/firewall/address-mask.xml.i>
+ #include <include/firewall/fqdn.xml.i>
+ #include <include/firewall/geoip.xml.i>
+ #include <include/firewall/mac-address.xml.i>
+ #include <include/firewall/port.xml.i>
+ #include <include/firewall/source-destination-group.xml.i>
+ </children>
+</node>
+<node name="icmp">
+ <properties>
+ <help>ICMP type and code information</help>
+ </properties>
+ <children>
+ <leafNode name="code">
+ <properties>
+ <help>ICMP code</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>ICMP code (0-255)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>ICMP type</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>ICMP type (0-255)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/firewall/icmp-type-name.xml.i>
+ </children>
+</node>
+<leafNode name="jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv4 name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address.xml.i>
+ #include <include/firewall/address-mask.xml.i>
+ #include <include/firewall/fqdn.xml.i>
+ #include <include/firewall/geoip.xml.i>
+ #include <include/firewall/mac-address.xml.i>
+ #include <include/firewall/port.xml.i>
+ #include <include/firewall/source-destination-group.xml.i>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/common-rule-ipv6.xml.i b/interface-definitions/include/firewall/common-rule-ipv6.xml.i
new file mode 100644
index 000000000..6219557db
--- /dev/null
+++ b/interface-definitions/include/firewall/common-rule-ipv6.xml.i
@@ -0,0 +1,72 @@
+<!-- include start from firewall/common-rule-ipv6.xml.i -->
+#include <include/firewall/common-rule-inet.xml.i>
+#include <include/firewall/hop-limit.xml.i>
+<node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address-ipv6.xml.i>
+ #include <include/firewall/address-mask-ipv6.xml.i>
+ #include <include/firewall/fqdn.xml.i>
+ #include <include/firewall/geoip.xml.i>
+ #include <include/firewall/mac-address.xml.i>
+ #include <include/firewall/port.xml.i>
+ #include <include/firewall/source-destination-group-ipv6.xml.i>
+ </children>
+</node>
+<node name="icmpv6">
+ <properties>
+ <help>ICMPv6 type and code information</help>
+ </properties>
+ <children>
+ <leafNode name="code">
+ <properties>
+ <help>ICMPv6 code</help>
+ <valueHelp>
+ <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</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>ICMPv6 type (0-255)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/firewall/icmpv6-type-name.xml.i>
+ </children>
+</node>
+<leafNode name="jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv6 name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address-ipv6.xml.i>
+ #include <include/firewall/address-mask-ipv6.xml.i>
+ #include <include/firewall/fqdn.xml.i>
+ #include <include/firewall/geoip.xml.i>
+ #include <include/firewall/mac-address.xml.i>
+ #include <include/firewall/port.xml.i>
+ #include <include/firewall/source-destination-group-ipv6.xml.i>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/default-action-base-chains.xml.i b/interface-definitions/include/firewall/default-action-base-chains.xml.i
new file mode 100644
index 000000000..aa62abf3d
--- /dev/null
+++ b/interface-definitions/include/firewall/default-action-base-chains.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from firewall/default-action-base-chains.xml.i -->
+<leafNode name="default-action">
+ <properties>
+ <help>Default-action for rule-set</help>
+ <completionHelp>
+ <list>drop accept</list>
+ </completionHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop if no prior rules are hit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept if no prior rules are hit</description>
+ </valueHelp>
+ <constraint>
+ <regex>(drop|accept)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>accept</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/firewall-hashing-parameters.xml.i b/interface-definitions/include/firewall/firewall-hashing-parameters.xml.i
new file mode 100644
index 000000000..7f34de3ba
--- /dev/null
+++ b/interface-definitions/include/firewall/firewall-hashing-parameters.xml.i
@@ -0,0 +1,35 @@
+<!-- include start from firewall/firewall-hashing-parameters.xml.i -->
+<leafNode name="hash">
+ <properties>
+ <help>Define the parameters of the packet header to apply the hashing</help>
+ <completionHelp>
+ <list>source-address destination-address source-port destination-port random</list>
+ </completionHelp>
+ <valueHelp>
+ <format>source-address</format>
+ <description>Use source IP address for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>destination-address</format>
+ <description>Use destination IP address for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>source-port</format>
+ <description>Use source port for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>destination-port</format>
+ <description>Use destination port for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>random</format>
+ <description>Do not use information from ip header. Use random value.</description>
+ </valueHelp>
+ <constraint>
+ <regex>(source-address|destination-address|source-port|destination-port|random)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ <defaultValue>random</defaultValue>
+</leafNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
new file mode 100644
index 000000000..a63874cb0
--- /dev/null
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -0,0 +1,252 @@
+<!-- include start from firewall/global-options.xml.i -->
+<node name="global-options">
+ <properties>
+ <help>Global Options</help>
+ </properties>
+ <children>
+ <leafNode name="all-ping">
+ <properties>
+ <help>Policy for handling of all IPv4 ICMP echo requests</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable processing of all IPv4 ICMP echo requests</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable processing of all IPv4 ICMP echo requests</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>enable</defaultValue>
+ </leafNode>
+ <leafNode name="broadcast-ping">
+ <properties>
+ <help>Policy for handling broadcast IPv4 ICMP echo and timestamp requests</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable processing of broadcast IPv4 ICMP echo/timestamp requests</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable processing of broadcast IPv4 ICMP echo/timestamp requests</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ <leafNode name="ip-src-route">
+ <properties>
+ <help>Policy for handling IPv4 packets with source route option</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable processing of IPv4 packets with source route option</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable processing of IPv4 packets with source route option</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ <leafNode name="log-martians">
+ <properties>
+ <help>Policy for logging IPv4 packets with invalid addresses</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable logging of IPv4 packets with invalid addresses</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable logging of Ipv4 packets with invalid addresses</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>enable</defaultValue>
+ </leafNode>
+ <leafNode name="receive-redirects">
+ <properties>
+ <help>Policy for handling received IPv4 ICMP redirect messages</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable processing of received IPv4 ICMP redirect messages</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable processing of received IPv4 ICMP redirect messages</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ <leafNode name="resolver-cache">
+ <properties>
+ <help>Retains last successful value if domain resolution fails</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="resolver-interval">
+ <properties>
+ <help>Domain resolver update interval</help>
+ <valueHelp>
+ <format>u32:10-3600</format>
+ <description>Interval (seconds)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-3600"/>
+ </constraint>
+ </properties>
+ <defaultValue>300</defaultValue>
+ </leafNode>
+ <leafNode name="send-redirects">
+ <properties>
+ <help>Policy for sending IPv4 ICMP redirect messages</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable sending IPv4 ICMP redirect messages</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable sending IPv4 ICMP redirect messages</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>enable</defaultValue>
+ </leafNode>
+ <leafNode name="source-validation">
+ <properties>
+ <help>Policy for source validation by reversed path, as specified in RFC3704</help>
+ <completionHelp>
+ <list>strict loose disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>strict</format>
+ <description>Enable Strict Reverse Path Forwarding as defined in RFC3704</description>
+ </valueHelp>
+ <valueHelp>
+ <format>loose</format>
+ <description>Enable Loose Reverse Path Forwarding as defined in RFC3704</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>No source validation</description>
+ </valueHelp>
+ <constraint>
+ <regex>(strict|loose|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ <leafNode name="syn-cookies">
+ <properties>
+ <help>Policy for using TCP SYN cookies with IPv4</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable use of TCP SYN cookies with IPv4</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable use of TCP SYN cookies with IPv4</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>enable</defaultValue>
+ </leafNode>
+ <leafNode name="twa-hazards-protection">
+ <properties>
+ <help>RFC1337 TCP TIME-WAIT assasination hazards protection</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable RFC1337 TIME-WAIT hazards protection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable RFC1337 TIME-WAIT hazards protection</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ <leafNode name="ipv6-receive-redirects">
+ <properties>
+ <help>Policy for handling received ICMPv6 redirect messages</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable processing of received ICMPv6 redirect messages</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable processing of received ICMPv6 redirect messages</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ <leafNode name="ipv6-src-route">
+ <properties>
+ <help>Policy for handling IPv6 packets with routing extension header</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable processing of IPv6 packets with routing header type 2</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable processing of IPv6 packets with routing header</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/inbound-interface.xml.i b/interface-definitions/include/firewall/inbound-interface.xml.i
new file mode 100644
index 000000000..13df71de3
--- /dev/null
+++ b/interface-definitions/include/firewall/inbound-interface.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from firewall/inbound-interface.xml.i -->
+<node name="inbound-interface">
+ <properties>
+ <help>Match inbound-interface</help>
+ </properties>
+ <children>
+ #include <include/firewall/match-interface.xml.i>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv4-custom-name.xml.i b/interface-definitions/include/firewall/ipv4-custom-name.xml.i
new file mode 100644
index 000000000..9d6ecfaf2
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv4-custom-name.xml.i
@@ -0,0 +1,41 @@
+<!-- include start from firewall/ipv4-custom-name.xml.i -->
+<tagNode name="name">
+ <properties>
+ <help>IPv4 custom firewall</help>
+ <constraint>
+ <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/firewall/default-action.xml.i>
+ #include <include/firewall/enable-default-log.xml.i>
+ #include <include/generic-description.xml.i>
+ <leafNode name="default-jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv4 name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv4 Firewall custom rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv4.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/outbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
new file mode 100644
index 000000000..08ee96419
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
@@ -0,0 +1,36 @@
+<!-- include start from firewall/ipv4-hook-forward.xml.i -->
+<node name="forward">
+ <properties>
+ <help>IPv4 forward firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv4 firewall forward filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv4 Firewall forward filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv4.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/outbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv4-hook-input.xml.i b/interface-definitions/include/firewall/ipv4-hook-input.xml.i
new file mode 100644
index 000000000..32b0ec94f
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv4-hook-input.xml.i
@@ -0,0 +1,35 @@
+<!-- include start from firewall/ipv4-hook-input.xml.i -->
+<node name="input">
+ <properties>
+ <help>IPv4 input firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv4 firewall input filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv4 Firewall input filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv4.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv4-hook-output.xml.i b/interface-definitions/include/firewall/ipv4-hook-output.xml.i
new file mode 100644
index 000000000..d50d1e93b
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv4-hook-output.xml.i
@@ -0,0 +1,35 @@
+<!-- include start from firewall/ipv4-hook-output.xml.i -->
+<node name="output">
+ <properties>
+ <help>IPv4 output firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv4 firewall output filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv4 Firewall output filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv4.xml.i>
+ #include <include/firewall/outbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv4-hook-prerouting.xml.i b/interface-definitions/include/firewall/ipv4-hook-prerouting.xml.i
new file mode 100644
index 000000000..c38918375
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv4-hook-prerouting.xml.i
@@ -0,0 +1,85 @@
+<!-- include start from firewall/ipv4-hook-prerouting.xml.i -->
+<node name="prerouting">
+ <properties>
+ <help>IPv4 prerouting firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv4 firewall prerouting filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv4 Firewall prerouting filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv4.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ <leafNode name="jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv4 name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <node name="raw">
+ <properties>
+ <help>IPv4 firewall prerouting raw</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <leafNode name="default-jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv4 name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv4 Firewall prerouting raw rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv4-raw.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ <leafNode name="jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv4 name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv6-custom-name.xml.i b/interface-definitions/include/firewall/ipv6-custom-name.xml.i
new file mode 100644
index 000000000..81610babf
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv6-custom-name.xml.i
@@ -0,0 +1,41 @@
+<!-- include start from firewall/ipv6-custom-name.xml.i -->
+<tagNode name="name">
+ <properties>
+ <help>IPv6 custom firewall</help>
+ <constraint>
+ <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/firewall/default-action.xml.i>
+ #include <include/firewall/enable-default-log.xml.i>
+ #include <include/generic-description.xml.i>
+ <leafNode name="default-jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv6 name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv6 Firewall custom rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv6.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/outbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
new file mode 100644
index 000000000..20ab8dbe8
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
@@ -0,0 +1,36 @@
+<!-- include start from firewall/ipv6-hook-forward.xml.i -->
+<node name="forward">
+ <properties>
+ <help>IPv6 forward firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv6 firewall forward filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv6 Firewall forward filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv6.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/outbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv6-hook-input.xml.i b/interface-definitions/include/firewall/ipv6-hook-input.xml.i
new file mode 100644
index 000000000..e34958f28
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv6-hook-input.xml.i
@@ -0,0 +1,35 @@
+<!-- include start from firewall/ipv6-hook-input.xml.i -->
+<node name="input">
+ <properties>
+ <help>IPv6 input firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv6 firewall input filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv6 Firewall input filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv6.xml.i>
+ #include <include/firewall/inbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/ipv6-hook-output.xml.i b/interface-definitions/include/firewall/ipv6-hook-output.xml.i
new file mode 100644
index 000000000..eb4ea7ac3
--- /dev/null
+++ b/interface-definitions/include/firewall/ipv6-hook-output.xml.i
@@ -0,0 +1,35 @@
+<!-- include start from firewall/ipv6-hook-output.xml.i -->
+<node name="output">
+ <properties>
+ <help>IPv6 output firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>IPv6 firewall output filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>IPv6 Firewall output filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-ipv6.xml.i>
+ #include <include/firewall/outbound-interface.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/match-interface.xml.i b/interface-definitions/include/firewall/match-interface.xml.i
index 3e52422cf..7810f88ab 100644
--- a/interface-definitions/include/firewall/match-interface.xml.i
+++ b/interface-definitions/include/firewall/match-interface.xml.i
@@ -5,6 +5,21 @@
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt*</format>
+ <description>Interface name with wildcard</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!txt</format>
+ <description>Inverted interface name to match</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/interface-name-with-wildcard-and-inverted.xml.i>
+ </constraint>
</properties>
</leafNode>
<leafNode name="interface-group">
@@ -13,6 +28,14 @@
<completionHelp>
<path>firewall group interface-group</path>
</completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface-group name to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!txt</format>
+ <description>Inverted interface-group name to match</description>
+ </valueHelp>
</properties>
</leafNode>
<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/nat-balance.xml.i b/interface-definitions/include/firewall/nat-balance.xml.i
new file mode 100644
index 000000000..01793f06b
--- /dev/null
+++ b/interface-definitions/include/firewall/nat-balance.xml.i
@@ -0,0 +1,28 @@
+<!-- include start from firewall/nat-balance.xml.i -->
+<tagNode name="backend">
+ <properties>
+ <help>Translated IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="weight">
+ <properties>
+ <help>Set probability for this output value</help>
+ <valueHelp>
+ <format>u32:1-100</format>
+ <description>Set probability for this output value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 1-100"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/outbound-interface.xml.i b/interface-definitions/include/firewall/outbound-interface.xml.i
new file mode 100644
index 000000000..8654dfd80
--- /dev/null
+++ b/interface-definitions/include/firewall/outbound-interface.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from firewall/outbound-interface.xml.i -->
+<node name="outbound-interface">
+ <properties>
+ <help>Match outbound-interface</help>
+ </properties>
+ <children>
+ #include <include/firewall/match-interface.xml.i>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i
index 609af1a2b..5ca1d525f 100644
--- a/interface-definitions/include/interface/dhcpv6-options.xml.i
+++ b/interface-definitions/include/interface/dhcpv6-options.xml.i
@@ -95,6 +95,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="no-release">
+ <properties>
+ <help>Do not send a release message on client exit</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/interface/eapol.xml.i b/interface-definitions/include/interface/eapol.xml.i
index c4cdeae0c..a3206f2c7 100644
--- a/interface-definitions/include/interface/eapol.xml.i
+++ b/interface-definitions/include/interface/eapol.xml.i
@@ -4,7 +4,7 @@
<help>Extensible Authentication Protocol over Local Area Network</help>
</properties>
<children>
- #include <include/pki/ca-certificate.xml.i>
+ #include <include/pki/ca-certificate-multi.xml.i>
#include <include/pki/certificate-key.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/interface/ipv6-accept-dad.xml.i b/interface-definitions/include/interface/ipv6-accept-dad.xml.i
new file mode 100644
index 000000000..7554b270a
--- /dev/null
+++ b/interface-definitions/include/interface/ipv6-accept-dad.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from interface/ipv6-accept-dad.xml.i -->
+<leafNode name="accept-dad">
+ <properties>
+ <help>Accept Duplicate Address Detection</help>
+ <valueHelp>
+ <format>0</format>
+ <description>Disable DAD</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1</format>
+ <description>Enable DAD</description>
+ </valueHelp>
+ <valueHelp>
+ <format>2</format>
+ <description>Enable DAD - disable IPv6 if MAC-based duplicate link-local address found</description>
+ </valueHelp>
+ </properties>
+ <defaultValue>1</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i
index babe6d20f..3b9294dd0 100644
--- a/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i
+++ b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i
@@ -1,7 +1,7 @@
<!-- include start from interface/ipv6-dup-addr-detect-transmits.xml.i -->
<leafNode name="dup-addr-detect-transmits">
<properties>
- <help>Number of NS messages to send while performing DAD (default: 1)</help>
+ <help>Number of NS messages to send while performing DAD</help>
<valueHelp>
<format>u32:0</format>
<description>Disable Duplicate Address Dectection (DAD)</description>
@@ -14,5 +14,6 @@
<validator name="numeric" argument="--non-negative"/>
</constraint>
</properties>
+ <defaultValue>1</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/interface/ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i
index f740ce0c2..edb4a74f9 100644
--- a/interface-definitions/include/interface/ipv6-options.xml.i
+++ b/interface-definitions/include/interface/ipv6-options.xml.i
@@ -6,8 +6,10 @@
<children>
#include <include/interface/adjust-mss.xml.i>
#include <include/interface/disable-forwarding.xml.i>
+ #include <include/interface/ipv6-accept-dad.xml.i>
#include <include/interface/ipv6-address.xml.i>
#include <include/interface/ipv6-dup-addr-detect-transmits.xml.i>
+ #include <include/interface/source-validation.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/interface/mac-multi.xml.i b/interface-definitions/include/interface/mac-multi.xml.i
new file mode 100644
index 000000000..458372e67
--- /dev/null
+++ b/interface-definitions/include/interface/mac-multi.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from interface/mac-multi.xml.i -->
+<leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>macaddr</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/macsec-key.xml.i b/interface-definitions/include/interface/macsec-key.xml.i
new file mode 100644
index 000000000..5a857a612
--- /dev/null
+++ b/interface-definitions/include/interface/macsec-key.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from interface/macsec-key.xml.i -->
+<leafNode name="key">
+ <properties>
+ <help>MACsec static key</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>16-byte (128-bit) hex-string (32 hex-digits) for gcm-aes-128 or 32-byte (256-bit) hex-string (64 hex-digits) for gcm-aes-256</description>
+ </valueHelp>
+ <constraint>
+ <regex>[A-Fa-f0-9]{32}</regex>
+ <regex>[A-Fa-f0-9]{64}</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/netns.xml.i b/interface-definitions/include/interface/netns.xml.i
index 39f9118fa..fd6da8f37 100644
--- a/interface-definitions/include/interface/netns.xml.i
+++ b/interface-definitions/include/interface/netns.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Network namespace name</help>
<valueHelp>
- <format>text</format>
+ <format>txt</format>
<description>Network namespace name</description>
</valueHelp>
<completionHelp>
diff --git a/interface-definitions/include/interface/parameters-innerproto.xml.i b/interface-definitions/include/interface/parameters-innerproto.xml.i
new file mode 100644
index 000000000..9cafebd11
--- /dev/null
+++ b/interface-definitions/include/interface/parameters-innerproto.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from interface/parameters-innerproto.xml.i -->
+<leafNode name="innerproto">
+ <properties>
+ <help>Use IPv4 as inner protocol instead of Ethernet</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/per-client-thread.xml.i b/interface-definitions/include/interface/per-client-thread.xml.i
new file mode 100644
index 000000000..2fd19b5ce
--- /dev/null
+++ b/interface-definitions/include/interface/per-client-thread.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from interface/per-client-thread.xml.i -->
+<leafNode name="per-client-thread">
+ <properties>
+ <help>Process traffic from each client in a dedicated thread</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/xdp.xml.i b/interface-definitions/include/interface/xdp.xml.i
deleted file mode 100644
index 10223e766..000000000
--- a/interface-definitions/include/interface/xdp.xml.i
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- include start from interface/xdp.xml.i -->
-<leafNode name="xdp">
- <properties>
- <help>Enable eXpress Data Path</help>
- <valueless/>
- </properties>
-</leafNode>
-<!-- include end -->
diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i
index 7b3b8804e..6234e6195 100644
--- a/interface-definitions/include/nat-rule.xml.i
+++ b/interface-definitions/include/nat-rule.xml.i
@@ -25,6 +25,15 @@
</node>
#include <include/generic-disable-node.xml.i>
#include <include/nat-exclude.xml.i>
+ <node name="load-balance">
+ <properties>
+ <help>Apply NAT load balance</help>
+ </properties>
+ <children>
+ #include <include/firewall/firewall-hashing-parameters.xml.i>
+ #include <include/firewall/nat-balance.xml.i>
+ </children>
+ </node>
<leafNode name="log">
<properties>
<help>NAT rule logging</help>
diff --git a/interface-definitions/include/ospf/graceful-restart.xml.i b/interface-definitions/include/ospf/graceful-restart.xml.i
new file mode 100644
index 000000000..37d9a7f13
--- /dev/null
+++ b/interface-definitions/include/ospf/graceful-restart.xml.i
@@ -0,0 +1,67 @@
+<!-- include start from ospf/graceful-restart.xml.i -->
+<node name="graceful-restart">
+ <properties>
+ <help>Graceful Restart</help>
+ </properties>
+ <children>
+ <leafNode name="grace-period">
+ <properties>
+ <help>Maximum length of the grace period</help>
+ <valueHelp>
+ <format>u32:1-1800</format>
+ <description>Maximum length of the grace period in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 5-1800"/>
+ </constraint>
+ </properties>
+ <defaultValue>120</defaultValue>
+ </leafNode>
+ <node name="helper">
+ <properties>
+ <help>OSPF graceful-restart helpers</help>
+ </properties>
+ <children>
+ <node name="enable">
+ <properties>
+ <help>Enable helper support</help>
+ </properties>
+ <children>
+ <leafNode name="router-id">
+ <properties>
+ <help>Advertising Router-ID</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Router-ID in IP address format</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="planned-only">
+ <properties>
+ <help>Supported only planned restart</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="supported-grace-time">
+ <properties>
+ <help>Supported grace timer</help>
+ <valueHelp>
+ <format>u32:10-1800</format>
+ <description>Grace interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-1800"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i
index b7f22cb88..c4778e126 100644
--- a/interface-definitions/include/ospf/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospf/protocol-common-config.xml.i
@@ -1,4 +1,24 @@
<!-- include start from ospf/protocol-common-config.xml.i -->
+<node name="aggregation">
+ <properties>
+ <help>External route aggregation</help>
+ </properties>
+ <children>
+ <leafNode name="timer">
+ <properties>
+ <help>Delay timer</help>
+ <valueHelp>
+ <format>u32:5-1800</format>
+ <description>Timer interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 5-1800"/>
+ </constraint>
+ </properties>
+ <defaultValue>5</defaultValue>
+ </leafNode>
+ </children>
+</node>
<tagNode name="access-list">
<properties>
<help>Access list to filter networks in routing updates</help>
@@ -306,6 +326,19 @@
</children>
</tagNode>
#include <include/ospf/auto-cost.xml.i>
+<node name="capability">
+ <properties>
+ <help>Enable specific OSPF features</help>
+ </properties>
+ <children>
+ <leafNode name="opaque">
+ <properties>
+ <help>Opaque LSA</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
#include <include/ospf/default-information.xml.i>
<leafNode name="default-metric">
<properties>
@@ -319,6 +352,21 @@
</constraint>
</properties>
</leafNode>
+#include <include/ospf/graceful-restart.xml.i>
+<node name="graceful-restart">
+ <children>
+ <node name="helper">
+ <children>
+ <leafNode name="no-strict-lsa-checking">
+ <properties>
+ <help>Disable strict LSA check</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</node>
<leafNode name="maximum-paths">
<properties>
<help>Maximum multiple paths (ECMP)</help>
@@ -816,6 +864,38 @@
</leafNode>
</children>
</node>
+<tagNode name="summary-address">
+ <properties>
+ <help>External summary address</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>OSPF area number in dotted decimal notation</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="no-advertise">
+ <properties>
+ <help>Don not advertise summary route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="tag">
+ <properties>
+ <help>Router tag</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>Router tag value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
<node name="timers">
<properties>
<help>Adjust routing timers</help>
@@ -876,4 +956,4 @@
</node>
</children>
</node>
-<!-- include end --> \ No newline at end of file
+<!-- include end -->
diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
index a7de50638..4c3ca68e1 100644
--- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
@@ -107,6 +107,21 @@
</node>
</children>
</node>
+#include <include/ospf/graceful-restart.xml.i>
+<node name="graceful-restart">
+ <children>
+ <node name="helper">
+ <children>
+ <leafNode name="lsa-check-disable">
+ <properties>
+ <help>Disable strict LSA check</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</node>
<tagNode name="interface">
<properties>
<help>Enable routing on an IPv6 interface</help>
diff --git a/interface-definitions/include/policy/extended-community-value-list.xml.i b/interface-definitions/include/policy/extended-community-value-list.xml.i
index c79f78c67..33a279be1 100644
--- a/interface-definitions/include/policy/extended-community-value-list.xml.i
+++ b/interface-definitions/include/policy/extended-community-value-list.xml.i
@@ -12,4 +12,4 @@
</constraint>
<constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage>
<multi/>
- <!-- include end -->
+<!-- include end -->
diff --git a/interface-definitions/include/policy/tag.xml.i b/interface-definitions/include/policy/tag.xml.i
new file mode 100644
index 000000000..ec25b9391
--- /dev/null
+++ b/interface-definitions/include/policy/tag.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from policy/tag.xml.i -->
+<leafNode name="tag">
+ <properties>
+ <help>Route tag value</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Route tag</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/bandwidth.xml.i b/interface-definitions/include/qos/bandwidth.xml.i
index cc923f642..0e29b6499 100644
--- a/interface-definitions/include/qos/bandwidth.xml.i
+++ b/interface-definitions/include/qos/bandwidth.xml.i
@@ -27,7 +27,7 @@
<description>Terabits per second</description>
</valueHelp>
<valueHelp>
- <format>&lt;number&gt;%</format>
+ <format>&lt;number&gt;%%</format>
<description>Percentage of interface link speed</description>
</valueHelp>
<constraint>
diff --git a/interface-definitions/include/radius-server-auth-port.xml.i b/interface-definitions/include/radius-server-auth-port.xml.i
index 660fa540f..d9ea1d445 100644
--- a/interface-definitions/include/radius-server-auth-port.xml.i
+++ b/interface-definitions/include/radius-server-auth-port.xml.i
@@ -1,15 +1,6 @@
<!-- include start from radius-server-auth-port.xml.i -->
+#include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Authentication port</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- </properties>
<defaultValue>1812</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/radius-server-key.xml.i b/interface-definitions/include/radius-server-key.xml.i
index c6301646b..dd5cdb0c6 100644
--- a/interface-definitions/include/radius-server-key.xml.i
+++ b/interface-definitions/include/radius-server-key.xml.i
@@ -2,6 +2,14 @@
<leafNode name="key">
<properties>
<help>Shared secret key</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Password string (key)</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:ascii:]]{1,128}</regex>
+ </constraint>
+ <constraintErrorMessage>Password must be less then 128 characters</constraintErrorMessage>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/version/dns-dynamic-version.xml.i b/interface-definitions/include/version/dns-dynamic-version.xml.i
new file mode 100644
index 000000000..b25fc6e76
--- /dev/null
+++ b/interface-definitions/include/version/dns-dynamic-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/dns-dynamic-version.xml.i -->
+<syntaxVersion component='dns-dynamic' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i
index c32484542..dd21bfaca 100644
--- a/interface-definitions/include/version/firewall-version.xml.i
+++ b/interface-definitions/include/version/firewall-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/firewall-version.xml.i -->
-<syntaxVersion component='firewall' version='10'></syntaxVersion>
+<syntaxVersion component='firewall' version='11'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/interfaces-version.xml.i b/interface-definitions/include/version/interfaces-version.xml.i
index e5e81d316..3d11ce888 100644
--- a/interface-definitions/include/version/interfaces-version.xml.i
+++ b/interface-definitions/include/version/interfaces-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/interfaces-version.xml.i -->
-<syntaxVersion component='interfaces' version='28'></syntaxVersion>
+<syntaxVersion component='interfaces' version='30'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/ntp-version.xml.i b/interface-definitions/include/version/ntp-version.xml.i
index 9eafbf7f0..155c824dc 100644
--- a/interface-definitions/include/version/ntp-version.xml.i
+++ b/interface-definitions/include/version/ntp-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/ntp-version.xml.i -->
-<syntaxVersion component='ntp' version='2'></syntaxVersion>
+<syntaxVersion component='ntp' 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
index 626dd6cbc..1514b19ab 100644
--- a/interface-definitions/include/version/vrrp-version.xml.i
+++ b/interface-definitions/include/version/vrrp-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/vrrp-version.xml.i -->
-<syntaxVersion component='vrrp' version='3'></syntaxVersion>
+<syntaxVersion component='vrrp' version='4'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 14b1036b4..427e04a54 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -225,7 +225,6 @@
#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>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index e7c196c5c..3669336fd 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -204,7 +204,6 @@
#include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
#include <include/interface/vrf.xml.i>
- #include <include/interface/xdp.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index 330dadd95..29b563a09 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -36,6 +36,7 @@
#include <include/interface/parameters-df.xml.i>
#include <include/interface/parameters-tos.xml.i>
#include <include/interface/parameters-ttl.xml.i>
+ #include <include/interface/parameters-innerproto.xml.i>
</children>
</node>
<node name="ipv6">
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index 6bc28e44b..766b0bede 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -52,6 +52,28 @@
<valueless/>
</properties>
</leafNode>
+ <node name="static">
+ <properties>
+ <help>Use static keys for MACsec [static Secure Authentication Key (SAK) mode]</help>
+ </properties>
+ <children>
+ #include <include/interface/macsec-key.xml.i>
+ <tagNode name="peer">
+ <properties>
+ <help>MACsec peer name</help>
+ <constraint>
+ <regex>[^ ]{1,100}</regex>
+ </constraint>
+ <constraintErrorMessage>MACsec peer name exceeds limit of 100 characters</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ #include <include/interface/mac.xml.i>
+ #include <include/interface/macsec-key.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
<node name="mka">
<properties>
<help>MACsec Key Agreement protocol (MKA)</help>
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 4e061c3e6..831659250 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -285,6 +285,19 @@
</constraint>
</properties>
</leafNode>
+ <node name="offload">
+ <properties>
+ <help>Configurable offload options</help>
+ </properties>
+ <children>
+ <leafNode name="dco">
+ <properties>
+ <help>Enable data channel offload on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="openvpn-option">
<properties>
<help>Additional OpenVPN options. You must use the syntax of openvpn.conf in this text-field. Using this without proper knowledge may result in a crashed OpenVPN server. Check system log to look for errors.</help>
@@ -739,6 +752,16 @@
</completionHelp>
</properties>
</leafNode>
+ <leafNode name="peer-fingerprint">
+ <properties>
+ <multi/>
+ <help>Peer certificate SHA256 fingerprint</help>
+ <constraint>
+ <regex>[0-9a-fA-F]{2}:([0-9a-fA-F]{2}:){30}[0-9a-fA-F]{2}</regex>
+ </constraint>
+ <constraintErrorMessage>Peer certificate fingerprint must be a colon-separated SHA256 hex digest</constraintErrorMessage>
+ </properties>
+ </leafNode>
<leafNode name="tls-version-min">
<properties>
<help>Specify the minimum required TLS version</help>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index 6342b21cf..3c79cef28 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -5,7 +5,7 @@
<tagNode name="wireguard" owner="${vyos_conf_scripts_dir}/interfaces-wireguard.py">
<properties>
<help>WireGuard Interface</help>
- <priority>459</priority>
+ <priority>379</priority>
<constraint>
<regex>wg[0-9]+</regex>
</constraint>
@@ -59,6 +59,7 @@
</properties>
<children>
#include <include/generic-disable-node.xml.i>
+ #include <include/generic-description.xml.i>
<leafNode name="public-key">
<properties>
<help>base64 encoded public key</help>
@@ -119,6 +120,7 @@
</children>
</tagNode>
#include <include/interface/redirect.xml.i>
+ #include <include/interface/per-client-thread.xml.i>
#include <include/interface/vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index a9538d577..88b858c07 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -595,6 +595,49 @@
<help>Wireless security settings</help>
</properties>
<children>
+ <node name="station-address">
+ <properties>
+ <help>Station MAC address based authentication</help>
+ </properties>
+ <children>
+ <leafNode name="mode">
+ <properties>
+ <help>Select security operation mode</help>
+ <completionHelp>
+ <list>accept deny</list>
+ </completionHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept all clients unless found in deny list</description>
+ </valueHelp>
+ <valueHelp>
+ <format>deny</format>
+ <description>Deny all clients unless found in accept list</description>
+ </valueHelp>
+ <constraint>
+ <regex>(accept|deny)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>accept</defaultValue>
+ </leafNode>
+ <node name="accept">
+ <properties>
+ <help>Accept station MAC address</help>
+ </properties>
+ <children>
+ #include <include/interface/mac-multi.xml.i>
+ </children>
+ </node>
+ <node name="deny">
+ <properties>
+ <help>Deny station MAC address</help>
+ </properties>
+ <children>
+ #include <include/interface/mac-multi.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
<node name="wep">
<properties>
<help>Wired Equivalent Privacy (WEP) parameters</help>
@@ -778,6 +821,7 @@
</properties>
<defaultValue>monitor</defaultValue>
</leafNode>
+ #include <include/interface/per-client-thread.xml.i>
#include <include/interface/redirect.xml.i>
#include <include/interface/vif.xml.i>
#include <include/interface/vif-s.xml.i>
diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in
index 501ff05d3..a06ceefb6 100644
--- a/interface-definitions/nat.xml.in
+++ b/interface-definitions/nat.xml.in
@@ -44,6 +44,14 @@
</leafNode>
#include <include/nat-translation-port.xml.i>
#include <include/nat-translation-options.xml.i>
+ <node name="redirect">
+ <properties>
+ <help>Redirect to local host</help>
+ </properties>
+ <children>
+ #include <include/nat-translation-port.xml.i>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/interface-definitions/netns.xml.in b/interface-definitions/netns.xml.in
index 87880e96a..5d958968f 100644
--- a/interface-definitions/netns.xml.in
+++ b/interface-definitions/netns.xml.in
@@ -3,7 +3,7 @@
<node name="netns" owner="${vyos_conf_scripts_dir}/netns.py">
<properties>
<help>Network namespace</help>
- <priority>299</priority>
+ <priority>291</priority>
</properties>
<children>
<tagNode name="name">
diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in
index 2275dd61c..4e874434b 100644
--- a/interface-definitions/ntp.xml.in
+++ b/interface-definitions/ntp.xml.in
@@ -57,7 +57,7 @@
</children>
</tagNode>
#include <include/allow-client.xml.i>
- #include <include/generic-interface-multi.xml.i>
+ #include <include/generic-interface.xml.i>
#include <include/listen-address.xml.i>
#include <include/interface/vrf.xml.i>
</children>
diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in
index aa39950c2..c470cfdb3 100644
--- a/interface-definitions/policy.xml.in
+++ b/interface-definitions/policy.xml.in
@@ -1052,18 +1052,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="tag">
- <properties>
- <help>Route tag to match</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Route tag</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/policy/tag.xml.i>
</children>
</node>
<node name="on-match">
@@ -1548,18 +1537,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="tag">
- <properties>
- <help>Tag value for routing protocol</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Tag value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/policy/tag.xml.i>
<leafNode name="weight">
<properties>
<help>BGP weight attribute</help>
diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in
index 43ca659e9..831601fc6 100644
--- a/interface-definitions/protocols-mpls.xml.in
+++ b/interface-definitions/protocols-mpls.xml.in
@@ -6,7 +6,7 @@
<node name="mpls" owner="${vyos_conf_scripts_dir}/protocols_mpls.py">
<properties>
<help>Multiprotocol Label Switching (MPLS)</help>
- <priority>400</priority>
+ <priority>490</priority>
</properties>
<children>
<node name="ldp">
diff --git a/interface-definitions/service-config-sync.xml.in b/interface-definitions/service-config-sync.xml.in
new file mode 100644
index 000000000..e804e17f7
--- /dev/null
+++ b/interface-definitions/service-config-sync.xml.in
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="config-sync" owner="${vyos_conf_scripts_dir}/service_config_sync.py">
+ <properties>
+ <help>Configuration synchronization</help>
+ </properties>
+ <children>
+ <node name="secondary">
+ <properties>
+ <help>Secondary server parameters</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>FQDN address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ <validator name="fqdn"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Connection API timeout</help>
+ <valueHelp>
+ <format>u32:1-300</format>
+ <description>Connection API timeout</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-300"/>
+ </constraint>
+ </properties>
+ <defaultValue>60</defaultValue>
+ </leafNode>
+ <leafNode name="key">
+ <properties>
+ <help>HTTP API key</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="mode">
+ <properties>
+ <help>Synchronization mode</help>
+ <completionHelp>
+ <list>load set</list>
+ </completionHelp>
+ <valueHelp>
+ <format>load</format>
+ <description>Load and replace configuration section</description>
+ </valueHelp>
+ <valueHelp>
+ <format>set</format>
+ <description>Set configuration section</description>
+ </valueHelp>
+ <constraint>
+ <regex>(load|set)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="section">
+ <properties>
+ <help>Section for synchronization</help>
+ <completionHelp>
+ <list>nat nat66 firewall</list>
+ </completionHelp>
+ <valueHelp>
+ <format>nat</format>
+ <description>NAT</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nat66</format>
+ <description>NAT66</description>
+ </valueHelp>
+ <valueHelp>
+ <format>firewall</format>
+ <description>firewall</description>
+ </valueHelp>
+ <constraint>
+ <regex>(nat|nat66|firewall)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/service-ids-ddos-protection.xml.in b/interface-definitions/service-ids-ddos-protection.xml.in
index bb06189bc..78463136b 100644
--- a/interface-definitions/service-ids-ddos-protection.xml.in
+++ b/interface-definitions/service-ids-ddos-protection.xml.in
@@ -70,17 +70,34 @@
<multi/>
</properties>
</leafNode>
- <node name="mode">
+ <leafNode name="mode">
<properties>
- <help>Traffic capture modes</help>
+ <help>Traffic capture mode</help>
+ <completionHelp>
+ <list>mirror sflow</list>
+ </completionHelp>
+ <valueHelp>
+ <format>mirror</format>
+ <description>Listen to mirrored traffic</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sflow</format>
+ <description>Capture sFlow flows</description>
+ </valueHelp>
+ <constraint>
+ <regex>(mirror|sflow)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="sflow">
+ <properties>
+ <help>Sflow settings</help>
</properties>
<children>
- <!-- Future modes "mirror" "netflow" "combine (both)" -->
- <leafNode name="mirror">
- <properties>
- <help>Listen mirrored traffic mode</help>
- <valueless/>
- </properties>
+ #include <include/listen-address-ipv4-single.xml.i>
+ #include <include/port-number.xml.i>
+ <leafNode name="port">
+ <defaultValue>6343</defaultValue>
</leafNode>
</children>
</node>
diff --git a/interface-definitions/service-mdns-repeater.xml.in b/interface-definitions/service-mdns-repeater.xml.in
index 4aab9a558..653dbbbe4 100644
--- a/interface-definitions/service-mdns-repeater.xml.in
+++ b/interface-definitions/service-mdns-repeater.xml.in
@@ -38,6 +38,7 @@
<constraint>
<regex>[-_.a-zA-Z0-9]+</regex>
</constraint>
+ <constraintErrorMessage>Service name must be alphanumeric and can contain hyphens and underscores</constraintErrorMessage>
<multi/>
</properties>
</leafNode>
diff --git a/interface-definitions/service-monitoring-zabbix-agent.xml.in b/interface-definitions/service-monitoring-zabbix-agent.xml.in
new file mode 100644
index 000000000..40f2df642
--- /dev/null
+++ b/interface-definitions/service-monitoring-zabbix-agent.xml.in
@@ -0,0 +1,193 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="monitoring">
+ <children>
+ <node name="zabbix-agent" owner="${vyos_conf_scripts_dir}/service_monitoring_zabbix-agent.py">
+ <properties>
+ <help>Zabbix-agent settings</help>
+ </properties>
+ <children>
+ <leafNode name="directory">
+ <properties>
+ <help>Folder containing individual Zabbix-agent configuration files</help>
+ <constraint>
+ <validator name="file-path" argument="--directory"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>Zabbix agent hostname</help>
+ <constraint>
+ #include <include/constraint/host-name.xml.i>
+ </constraint>
+ <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="limits">
+ <properties>
+ <help>Limit settings</help>
+ </properties>
+ <children>
+ <leafNode name="buffer-flush-interval">
+ <properties>
+ <help>Do not keep data longer than N seconds in buffer</help>
+ <valueHelp>
+ <format>u32:1-3600</format>
+ <description>Seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-3600"/>
+ </constraint>
+ <constraintErrorMessage>buffer-flush-interval must be between 1 and 3600 seconds</constraintErrorMessage>
+ </properties>
+ <defaultValue>5</defaultValue>
+ </leafNode>
+ <leafNode name="buffer-size">
+ <properties>
+ <help>Maximum number of values in a memory buffer</help>
+ <valueHelp>
+ <format>u32:2-65535</format>
+ <description>Maximum number of values in a memory buffer</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-65535"/>
+ </constraint>
+ <constraintErrorMessage>Buffer-size must be between 2 and 65535</constraintErrorMessage>
+ </properties>
+ <defaultValue>100</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ <node name="log">
+ <properties>
+ <help>Log settings</help>
+ </properties>
+ <children>
+ <leafNode name="debug-level">
+ <properties>
+ <help>Debug level</help>
+ <completionHelp>
+ <list>basic critical error warning debug extended-debug</list>
+ </completionHelp>
+ <valueHelp>
+ <format>basic</format>
+ <description>Basic information</description>
+ </valueHelp>
+ <valueHelp>
+ <format>critical</format>
+ <description>Critical information</description>
+ </valueHelp>
+ <valueHelp>
+ <format>error</format>
+ <description>Error information</description>
+ </valueHelp>
+ <valueHelp>
+ <format>warning</format>
+ <description>Warnings</description>
+ </valueHelp>
+ <valueHelp>
+ <format>debug</format>
+ <description>Debug information</description>
+ </valueHelp>
+ <valueHelp>
+ <format>extended-debug</format>
+ <description>Extended debug information</description>
+ </valueHelp>
+ <constraint>
+ <regex>(basic|critical|error|warning|debug|extended-debug)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>warning</defaultValue>
+ </leafNode>
+ <leafNode name="remote-commands">
+ <properties>
+ <help>Enable logging of executed shell commands as warnings</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="size">
+ <properties>
+ <help>Log file size in megabytes</help>
+ <valueHelp>
+ <format>u32:0-1024</format>
+ <description>Megabytes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-1024"/>
+ </constraint>
+ <constraintErrorMessage>Size must be between 0 and 1024 Megabytes</constraintErrorMessage>
+ </properties>
+ <defaultValue>0</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ #include <include/listen-address.xml.i>
+ <leafNode name="listen-address">
+ <defaultValue>0.0.0.0</defaultValue>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ <leafNode name="port">
+ <defaultValue>10050</defaultValue>
+ </leafNode>
+ <leafNode name="server">
+ <properties>
+ <help>Remote server to connect to</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Server IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Server IPv6 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>Server hostname/FQDN</description>
+ </valueHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ <tagNode name="server-active">
+ <properties>
+ <help>Remote server address to get active checks from</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Server IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Server IPv6 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>Server hostname/FQDN</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/port-number.xml.i>
+ </children>
+ </tagNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Item processing timeout in seconds</help>
+ <valueHelp>
+ <format>u32:1-30</format>
+ <description>Item processing timeout</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-30"/>
+ </constraint>
+ <constraintErrorMessage>Timeout must be between 1 and 30 seconds</constraintErrorMessage>
+ </properties>
+ <defaultValue>3</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/system-ip.xml.in b/interface-definitions/system-ip.xml.in
index abdede979..6db4dbfc7 100644
--- a/interface-definitions/system-ip.xml.in
+++ b/interface-definitions/system-ip.xml.in
@@ -48,6 +48,64 @@
</leafNode>
</children>
</node>
+ <node name="tcp">
+ <properties>
+ <help>IPv4 TCP parameters</help>
+ </properties>
+ <children>
+ <node name="mss">
+ <properties>
+ <help>IPv4 TCP MSS probing options</help>
+ </properties>
+ <children>
+ <leafNode name="probing">
+ <properties>
+ <help>Attempt to lower the MSS if TCP connections fail to establish</help>
+ <completionHelp>
+ <list>on-icmp-black-hole force</list>
+ </completionHelp>
+ <valueHelp>
+ <format>on-icmp-black-hole</format>
+ <description>Attempt TCP MSS probing when an ICMP black hole is detected</description>
+ </valueHelp>
+ <valueHelp>
+ <format>force</format>
+ <description>Attempt TCP MSS probing by default</description>
+ </valueHelp>
+ <constraint>
+ <regex>(on-icmp-black-hole|force)</regex>
+ </constraint>
+ <constraintErrorMessage>Must be on-icmp-black-hole or force</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="base">
+ <properties>
+ <help>Base MSS to start probing from (applicable to "probing force")</help>
+ <valueHelp>
+ <format>u32:48-1460</format>
+ <description>Base MSS value for probing (default: 1024)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 48-1460"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="floor">
+ <properties>
+ <help>Minimum MSS to stop probing at (default: 48)</help>
+ <valueHelp>
+ <format>u32:48-1460</format>
+ <description>Minimum MSS value to probe</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 48-1460"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
#include <include/system-ip-protocol.xml.i>
</children>
</node>
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
index be4f53c3b..71db8b1d6 100644
--- a/interface-definitions/system-login.xml.in
+++ b/interface-definitions/system-login.xml.in
@@ -184,6 +184,13 @@
<leafNode name="home-directory">
<properties>
<help>Home directory</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Path to home directory</description>
+ </valueHelp>
+ <constraint>
+ <regex>\/$|(\/[a-zA-Z_0-9-.]+)+</regex>
+ </constraint>
</properties>
</leafNode>
</children>
@@ -193,20 +200,7 @@
<children>
<tagNode name="server">
<children>
- <leafNode name="timeout">
- <properties>
- <help>Session timeout</help>
- <valueHelp>
- <format>u32:1-30</format>
- <description>Session timeout in seconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-30"/>
- </constraint>
- <constraintErrorMessage>Timeout must be between 1 and 30 seconds</constraintErrorMessage>
- </properties>
- <defaultValue>2</defaultValue>
- </leafNode>
+ #include <include/radius-timeout.xml.i>
<leafNode name="priority">
<properties>
<help>Server priority</help>
@@ -225,6 +219,50 @@
#include <include/interface/vrf.xml.i>
</children>
</node>
+ <node name="tacacs">
+ <properties>
+ <help>TACACS+ based user authentication</help>
+ </properties>
+ <children>
+ <tagNode name="server">
+ <properties>
+ <help>TACACS+ server configuration</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>TACACS+ server IPv4 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ #include <include/radius-server-key.xml.i>
+ #include <include/port-number.xml.i>
+ <leafNode name="port">
+ <defaultValue>49</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="source-address">
+ <properties>
+ <help>Source IP used to communicate with TACACS+ server</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --ipv4</script>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 source address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/radius-timeout.xml.i>
+ #include <include/interface/vrf.xml.i>
+ </children>
+ </node>
<leafNode name="max-login-session">
<properties>
<help>Maximum number of all login sessions</help>
diff --git a/interface-definitions/system-option.xml.in b/interface-definitions/system-option.xml.in
index 0fa349e0b..b1b5f7fae 100644
--- a/interface-definitions/system-option.xml.in
+++ b/interface-definitions/system-option.xml.in
@@ -36,7 +36,7 @@
<properties>
<help>System keyboard layout, type ISO2</help>
<completionHelp>
- <list>us uk fr de es fi jp106 no dk dvorak</list>
+ <list>us uk fr de es fi jp106 no dk se-latin1 dvorak</list>
</completionHelp>
<valueHelp>
<format>us</format>
@@ -75,11 +75,15 @@
<description>Denmark</description>
</valueHelp>
<valueHelp>
+ <format>se-latin1</format>
+ <description>Sweden</description>
+ </valueHelp>
+ <valueHelp>
<format>dvorak</format>
<description>Dvorak</description>
</valueHelp>
<constraint>
- <regex>(us|uk|fr|de|es|fi|jp106|no|dk|dvorak)</regex>
+ <regex>(us|uk|fr|de|es|fi|jp106|no|dk|se-latin1|dvorak)</regex>
</constraint>
<constraintErrorMessage>Invalid keyboard layout</constraintErrorMessage>
</properties>
@@ -140,6 +144,26 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="time-format">
+ <properties>
+ <help>System time-format</help>
+ <completionHelp>
+ <list>12-hour 24-hour</list>
+ </completionHelp>
+ <valueHelp>
+ <format>12-hour</format>
+ <description>12 hour time format</description>
+ </valueHelp>
+ <valueHelp>
+ <format>24-hour</format>
+ <description>24 hour time format</description>
+ </valueHelp>
+ <constraint>
+ <regex>(12-hour|24-hour)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>12-hour</defaultValue>
+ </leafNode>
</children>
</node>
</children>
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in
new file mode 100644
index 000000000..3f0758c0a
--- /dev/null
+++ b/interface-definitions/vpp.xml.in
@@ -0,0 +1,342 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="vpp" owner="${vyos_conf_scripts_dir}/vpp.py">
+ <properties>
+ <help>Accelerated data-plane</help>
+ <priority>295</priority>
+ </properties>
+ <children>
+ <node name="cpu">
+ <properties>
+ <help>CPU settings</help>
+ </properties>
+ <children>
+ <leafNode name="corelist-workers">
+ <properties>
+ <help>List of cores worker threads</help>
+ <valueHelp>
+ <format>&lt;id&gt;</format>
+ <description>CPU core id</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;idN&gt;-&lt;idM&gt;</format>
+ <description>CPU core id range (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-512"/>
+ </constraint>
+ <constraintErrorMessage>not a valid CPU core value or range</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="main-core">
+ <properties>
+ <help>Main core</help>
+ <valueHelp>
+ <format>u32:0-512</format>
+ <description>Assign main thread to specific core</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-512"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="skip-cores">
+ <properties>
+ <help>Skip cores</help>
+ <valueHelp>
+ <format>u32:0-512</format>
+ <description>Skip cores</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-512"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="workers">
+ <properties>
+ <help>Create worker threads</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Worker threads</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-512"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="interface">
+ <properties>
+ <help>Interface</help>
+ <valueHelp>
+ <format>ethN</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ <regex>((eth|lan)[0-9]+|(eno|ens|enp|enx).+)</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid interface name</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="num-rx-desc">
+ <properties>
+ <help>Number of receive ring descriptors</help>
+ <valueHelp>
+ <format>u32:256-8192</format>
+ <description>Number of receive ring descriptors</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 256-8192"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="num-tx-desc">
+ <properties>
+ <help>Number of tranceive ring descriptors</help>
+ <valueHelp>
+ <format>u32:256-8192</format>
+ <description>Number of tranceive ring descriptors</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 256-8192"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="num-rx-queues">
+ <properties>
+ <help>Number of receive ring descriptors</help>
+ <valueHelp>
+ <format>u32:256-8192</format>
+ <description>Number of receive queues</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 256-8192"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="num-tx-queues">
+ <properties>
+ <help>Number of tranceive ring descriptors</help>
+ <valueHelp>
+ <format>u32:256-8192</format>
+ <description>Number of tranceive queues</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 256-8192"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name='pci'>
+ <properties>
+ <help>PCI address allocation</help>
+ <valueHelp>
+ <format>auto</format>
+ <description>Auto detect PCI address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;xxxx:xx:xx.x&gt;</format>
+ <description>Set Peripheral Component Interconnect (PCI) address</description>
+ </valueHelp>
+ <constraint>
+ <regex>(auto|[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])</regex>
+ </constraint>
+ </properties>
+ <defaultValue>auto</defaultValue>
+ </leafNode>
+ <leafNode name="rx-mode">
+ <properties>
+ <help>Receive packet processing mode</help>
+ <completionHelp>
+ <list>polling interrupt adaptive</list>
+ </completionHelp>
+ <valueHelp>
+ <format>polling</format>
+ <description>Constantly check for new data</description>
+ </valueHelp>
+ <valueHelp>
+ <format>interrupt</format>
+ <description>Interrupt mode</description>
+ </valueHelp>
+ <valueHelp>
+ <format>adaptive</format>
+ <description>Adaptive mode</description>
+ </valueHelp>
+ <constraint>
+ <regex>(polling|interrupt|adaptive)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="ip">
+ <properties>
+ <help>IP settings</help>
+ </properties>
+ <children>
+ <leafNode name="heap-size">
+ <properties>
+ <help>IPv4 heap size</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Amount of memory (in Mbytes) dedicated to the destination IP lookup table</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ <defaultValue>32</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ <node name="ip6">
+ <properties>
+ <help>IPv6 settings</help>
+ </properties>
+ <children>
+ <leafNode name="heap-size">
+ <properties>
+ <help>IPv6 heap size</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Amount of memory (in Mbytes) dedicated to the destination IP lookup table</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ <defaultValue>32</defaultValue>
+ </leafNode>
+ <leafNode name="hash-buckets">
+ <properties>
+ <help>IPv6 forwarding table hash buckets</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>IPv6 forwarding table hash buckets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ <defaultValue>65536</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ <node name="l2learn">
+ <properties>
+ <help>Level 2 MAC address learning settings</help>
+ </properties>
+ <children>
+ <leafNode name="limit">
+ <properties>
+ <help>Number of MAC addresses in the L2 FIB</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>Number of concurent entries</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ <defaultValue>4194304</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ <node name="logging">
+ <properties>
+ <help>Loggint settings</help>
+ </properties>
+ <children>
+ <leafNode name="default-log-level">
+ <properties>
+ <help>default-log-level</help>
+ <completionHelp>
+ <list>alert crit debug disabled emerg err info notice warn</list>
+ </completionHelp>
+ <valueHelp>
+ <format>alert</format>
+ <description>Alert</description>
+ </valueHelp>
+ <valueHelp>
+ <format>crit</format>
+ <description>Critical</description>
+ </valueHelp>
+ <valueHelp>
+ <format>debug</format>
+ <description>Debug</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disabled</format>
+ <description>Disabled</description>
+ </valueHelp>
+ <valueHelp>
+ <format>emerg</format>
+ <description>Emergency</description>
+ </valueHelp>
+ <valueHelp>
+ <format>err</format>
+ <description>Error</description>
+ </valueHelp>
+ <valueHelp>
+ <format>info</format>
+ <description>Informational</description>
+ </valueHelp>
+ <valueHelp>
+ <format>notice</format>
+ <description>Notice</description>
+ </valueHelp>
+ <valueHelp>
+ <format>warn</format>
+ <description>Warning</description>
+ </valueHelp>
+ <constraint>
+ <regex>(alert|crit|debug|disabled|emerg|err|info|notice|warn)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="physmem">
+ <properties>
+ <help>Memory settings</help>
+ </properties>
+ <children>
+ <leafNode name="max-size">
+ <properties>
+ <help>Set memory size for protectable memory allocator (pmalloc) memory space</help>
+ <valueHelp>
+ <format>&lt;number&gt;m</format>
+ <description>Megabyte</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;number&gt;g</format>
+ <description>Gigabyte</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="unix">
+ <properties>
+ <help>Unix settings</help>
+ </properties>
+ <children>
+ <leafNode name="poll-sleep-usec">
+ <properties>
+ <help>Add a fixed-sleep between main loop poll</help>
+ <valueHelp>
+ <format>u32:0-4294967295</format>
+ <description>Number of receive queues</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ <defaultValue>0</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in
index e05f64643..8c9e816d1 100644
--- a/interface-definitions/xml-component-version.xml.in
+++ b/interface-definitions/xml-component-version.xml.in
@@ -10,6 +10,7 @@
#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-dynamic-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>
diff --git a/op-mode-definitions/clear-dhcp-server-lease.xml.in b/op-mode-definitions/clear-dhcp-server-lease.xml.in
index b1241588c..aef0eb22a 100644
--- a/op-mode-definitions/clear-dhcp-server-lease.xml.in
+++ b/op-mode-definitions/clear-dhcp-server-lease.xml.in
@@ -4,7 +4,7 @@
<children>
<node name="dhcp-server">
<properties>
- <help>clear DHCP server lease</help>
+ <help>Clear DHCP server lease</help>
</properties>
<children>
<tagNode name="lease">
diff --git a/op-mode-definitions/configure.xml.in b/op-mode-definitions/configure.xml.in
index 3dd5a0f45..a711fa4a9 100644
--- a/op-mode-definitions/configure.xml.in
+++ b/op-mode-definitions/configure.xml.in
@@ -11,7 +11,12 @@
echo "Please do it as an administrator level VyOS user instead."
else
if grep -q -e '^overlay.*/filesystem.squashfs' /proc/mounts; then
- echo "WARNING: You are currently configuring a live-ISO environment, changes will not persist until installed"
+ echo "WARNING: You are currently configuring a live-ISO environment, changes will not persist until installed"
+ else
+ if grep -q -s '1' /tmp/vyos-config-status; then
+ echo "WARNING: There was a config error on boot: saving the configuration now could overwrite data."
+ echo "You may want to check and reload the boot config"
+ fi
fi
history -w
export _OFR_CONFIGURE=ok
diff --git a/op-mode-definitions/container.xml.in b/op-mode-definitions/container.xml.in
index ada9a4d59..f581d39fa 100644
--- a/op-mode-definitions/container.xml.in
+++ b/op-mode-definitions/container.xml.in
@@ -83,7 +83,7 @@
<children>
<tagNode name="container">
<properties>
- <help>Monitor last lines of container logs</help>
+ <help>Monitor last lines of container log</help>
<completionHelp>
<path>container name</path>
</completionHelp>
diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in
index 66584efc3..6855fe447 100644
--- a/op-mode-definitions/dhcp.xml.in
+++ b/op-mode-definitions/dhcp.xml.in
@@ -7,6 +7,30 @@
<help>Show DHCP (Dynamic Host Configuration Protocol) information</help>
</properties>
<children>
+ <node name="client">
+ <properties>
+ <help>Show DHCP client information</help>
+ </properties>
+ <children>
+ <node name="leases">
+ <properties>
+ <help>Show DHCP client leases</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help> Show DHCP client information for a given interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces --broadcast</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/dhcp.py show_client_leases --family inet --interface $6</command>
+ </tagNode>
+ </children>
+ <command>${vyos_op_scripts_dir}/dhcp.py show_client_leases --family inet</command>
+ </node>
+ </children>
+ </node>
<node name="server">
<properties>
<help>Show DHCP server information</help>
diff --git a/op-mode-definitions/dns-dynamic.xml.in b/op-mode-definitions/dns-dynamic.xml.in
index 9c37874fb..79478f392 100644
--- a/op-mode-definitions/dns-dynamic.xml.in
+++ b/op-mode-definitions/dns-dynamic.xml.in
@@ -1,16 +1,64 @@
<?xml version="1.0"?>
<interfaceDefinition>
+ <node name="clear">
+ <children>
+ <node name="dns">
+ <properties>
+ <help>Clear Domain Name System</help>
+ </properties>
+ <children>
+ <node name="dynamic">
+ <properties>
+ <help>Clear Dynamic DNS information</help>
+ </properties>
+ <children>
+ <leafNode name="cache">
+ <properties>
+ <help>Clear Dynamic DNS information cache (ddclient)</help>
+ </properties>
+ <command>sudo rm -f /run/ddclient/ddclient.cache</command>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="monitor">
+ <children>
+ <node name="log">
+ <children>
+ <node name="dns">
+ <properties>
+ <help>Monitor last lines of Domain Name System related services</help>
+ </properties>
+ <children>
+ <node name="dynamic">
+ <properties>
+ <help>Monitor last lines of Dynamic DNS update service</help>
+ </properties>
+ <command>journalctl --no-hostname --follow --boot --unit ddclient.service</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
<node name="show">
<children>
<node name="log">
<children>
<node name="dns">
+ <properties>
+ <help>Show log for Domain Name System related services</help>
+ </properties>
<children>
<node name="dynamic">
<properties>
- <help>Show log for dynamic DNS</help>
+ <help>Show log for Dynamic DNS update service</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e "ddclient"</command>
+ <command>journalctl --no-hostname --boot --unit ddclient.service</command>
</node>
</children>
</node>
@@ -18,7 +66,7 @@
</node>
<node name="dns">
<properties>
- <help>Show DNS information</help>
+ <help>Show Domain Name System related information</help>
</properties>
<children>
<node name="dynamic">
@@ -30,7 +78,7 @@
<properties>
<help>Show Dynamic DNS status</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/dynamic_dns.py --status</command>
+ <command>sudo ${vyos_op_scripts_dir}/dns_dynamic.py --status</command>
</leafNode>
</children>
</node>
@@ -41,12 +89,15 @@
<node name="restart">
<children>
<node name="dns">
+ <properties>
+ <help>Restart specific Domain Name System related service</help>
+ </properties>
<children>
<node name="dynamic">
<properties>
<help>Restart Dynamic DNS service</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/dynamic_dns.py --update</command>
+ <command>sudo ${vyos_op_scripts_dir}/dns_dynamic.py --update</command>
</node>
</children>
</node>
@@ -59,14 +110,14 @@
<children>
<node name="dns">
<properties>
- <help>Update DNS information</help>
+ <help>Update Domain Name System related information</help>
</properties>
<children>
<node name="dynamic">
<properties>
<help>Update Dynamic DNS information</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/dynamic_dns.py --update</command>
+ <command>sudo ${vyos_op_scripts_dir}/dns_dynamic.py --update</command>
</node>
</children>
</node>
diff --git a/op-mode-definitions/dns-forwarding.xml.in b/op-mode-definitions/dns-forwarding.xml.in
index c8ca117be..a4c650c38 100644
--- a/op-mode-definitions/dns-forwarding.xml.in
+++ b/op-mode-definitions/dns-forwarding.xml.in
@@ -6,7 +6,7 @@
<children>
<node name="dns">
<properties>
- <help>Monitor last lines of Domain Name Service (DNS)</help>
+ <help>Monitor last lines of Domain Name System (DNS) related services</help>
</properties>
<children>
<node name="forwarding">
@@ -27,7 +27,7 @@
<children>
<node name="dns">
<properties>
- <help>Show log for Domain Name Service (DNS)</help>
+ <help>Show log for Domain Name System (DNS) related services</help>
</properties>
<children>
<node name="forwarding">
@@ -42,7 +42,7 @@
</node>
<node name="dns">
<properties>
- <help>Show DNS information</help>
+ <help>Show Domain Name System (DNS) related information</help>
</properties>
<children>
<node name="forwarding">
@@ -66,7 +66,7 @@
<children>
<node name="dns">
<properties>
- <help>Restart specific DNS service</help>
+ <help>Restart specific Domain Name System (DNS) related service</help>
</properties>
<children>
<leafNode name="forwarding">
@@ -83,7 +83,7 @@
<children>
<node name="dns">
<properties>
- <help>Reset a DNS service state</help>
+ <help>Reset Domain Name System (DNS) related service state</help>
</properties>
<children>
<node name="forwarding">
diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in
index b5dee7c9e..0f296c272 100644
--- a/op-mode-definitions/firewall.xml.in
+++ b/op-mode-definitions/firewall.xml.in
@@ -119,6 +119,7 @@
<path>firewall group address-group</path>
<path>firewall group network-group</path>
<path>firewall group port-group</path>
+ <path>firewall group interface-group</path>
<path>firewall group ipv6-address-group</path>
<path>firewall group ipv6-network-group</path>
</completionHelp>
@@ -131,46 +132,206 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group</command>
</leafNode>
- <tagNode name="ipv6-name">
+ <node name="ipv6">
<properties>
- <help>Show IPv6 firewall chains</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
+ <help>Show IPv6 firewall</help>
</properties>
<children>
- <tagNode name="rule">
+ <node name="forward">
+ <properties>
+ <help>Show IPv6 forward firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show IPv6 forward filter firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv6 forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ </node>
+ </children>
+ </node>
+ <node name="input">
+ <properties>
+ <help>Show IPv6 input firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show IPv6 forward input firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv6 input filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 input filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ </node>
+ </children>
+ </node>
+ <node name="output">
+ <properties>
+ <help>Show IPv6 output firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show IPv6 output filter firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv6 output filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 output filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ </node>
+ </children>
+ </node>
+ <tagNode name="ipv6-name">
<properties>
- <help>Show summary of IPv6 firewall rules</help>
+ <help>Show IPv6 custom firewall chains</help>
<completionHelp>
- <path>firewall ipv6-name ${COMP_WORDS[6]} rule</path>
+ <path>firewall ipv6 ipv6-name</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --name $4 --rule $6 --ipv6</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv6 custom firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6 ipv6-name ${COMP_WORDS[6]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --name $4 --ipv6</command>
- </tagNode>
- <tagNode name="name">
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
+ </node>
+ <node name="ipv4">
<properties>
- <help>Show IPv4 firewall chains</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
+ <help>Show IPv4 firewall</help>
</properties>
<children>
- <tagNode name="rule">
+ <node name="forward">
+ <properties>
+ <help>Show IPv4 forward firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show IPv4 forward filter firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv4 forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <node name="input">
+ <properties>
+ <help>Show IPv4 input firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show IPv4 forward input firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv4 input filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 input filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <node name="output">
+ <properties>
+ <help>Show IPv4 output firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show IPv4 output filter firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv4 output filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 output filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <tagNode name="name">
<properties>
- <help>Show summary of IPv4 firewall rules</help>
+ <help>Show IPv4 custom firewall chains</help>
<completionHelp>
- <path>firewall name ${COMP_WORDS[6]} rule</path>
+ <path>firewall ipv4 name</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --name $4 --rule $6</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv4 custom firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv4 name ${COMP_WORDS[6]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --name $4</command>
- </tagNode>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
+ </node>
<leafNode name="statistics">
<properties>
<help>Show statistics of firewall application</help>
diff --git a/op-mode-definitions/include/ospfv3/detail.xml.i b/op-mode-definitions/include/frr-detail.xml.i
index 4e3c91268..4edf82eab 100644
--- a/op-mode-definitions/include/ospfv3/detail.xml.i
+++ b/op-mode-definitions/include/frr-detail.xml.i
@@ -1,8 +1,8 @@
-<!-- included start from ospfv3/detail.xml.i -->
-<node name="detail">
+<!-- included start from frr-detail.xml.i -->
+<leafNode name="detail">
<properties>
<help>Show detailed information</help>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
-</node>
+</leafNode>
<!-- included end -->
diff --git a/op-mode-definitions/include/monitor-background.xml.i b/op-mode-definitions/include/monitor-background.xml.i
deleted file mode 100644
index 9931127e3..000000000
--- a/op-mode-definitions/include/monitor-background.xml.i
+++ /dev/null
@@ -1,21 +0,0 @@
-<!-- included start from monitor-background.xml.i -->
-<node name="background">
- <properties>
- <help>Monitor in background</help>
- </properties>
- <children>
- <node name="start">
- <properties>
- <help>Start background monitoring</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor-background ${3^^} ${3}</command>
- </node>
- <node name="stop">
- <properties>
- <help>Stop background monitoring</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor-background-stop ${3^^}</command>
- </node>
- </children>
-</node>
-<!-- included end -->
diff --git a/op-mode-definitions/include/ospf-common.xml.i b/op-mode-definitions/include/ospf/common.xml.i
index 979ffb07e..684073cc5 100644
--- a/op-mode-definitions/include/ospf-common.xml.i
+++ b/op-mode-definitions/include/ospf/common.xml.i
@@ -502,6 +502,7 @@
</tagNode>
</children>
</node>
+#include <include/ospf/graceful-restart.xml.i>
<node name="interface">
<properties>
<help>Show IPv4 OSPF interface information</help>
@@ -523,12 +524,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- <node name="detail">
- <properties>
- <help>Show detailed IPv4 OSPF neighbor information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<tagNode name="neighbor">
@@ -556,4 +552,3 @@
</children>
</node>
<!-- included end -->
-
diff --git a/op-mode-definitions/include/ospf/graceful-restart.xml.i b/op-mode-definitions/include/ospf/graceful-restart.xml.i
new file mode 100644
index 000000000..736d8f951
--- /dev/null
+++ b/op-mode-definitions/include/ospf/graceful-restart.xml.i
@@ -0,0 +1,13 @@
+<node name="graceful-restart">
+ <properties>
+ <help>Show IPv4 OSPF Graceful Restart</help>
+ </properties>
+ <children>
+ <leafNode name="helper">
+ <properties>
+ <help>OSPF Graceful Restart helper details</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+ </node>
diff --git a/op-mode-definitions/include/ospfv3/adv-router-id-node-tag.xml.i b/op-mode-definitions/include/ospfv3/adv-router-id-node-tag.xml.i
index a1bd67a90..806366444 100644
--- a/op-mode-definitions/include/ospfv3/adv-router-id-node-tag.xml.i
+++ b/op-mode-definitions/include/ospfv3/adv-router-id-node-tag.xml.i
@@ -8,7 +8,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
</children>
diff --git a/op-mode-definitions/include/ospfv3/adv-router.xml.i b/op-mode-definitions/include/ospfv3/adv-router.xml.i
index a14fc39db..238242d11 100644
--- a/op-mode-definitions/include/ospfv3/adv-router.xml.i
+++ b/op-mode-definitions/include/ospfv3/adv-router.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
</properties>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
diff --git a/op-mode-definitions/include/ospfv3/border-routers.xml.i b/op-mode-definitions/include/ospfv3/border-routers.xml.i
index b6fac6785..e8827a2c4 100644
--- a/op-mode-definitions/include/ospfv3/border-routers.xml.i
+++ b/op-mode-definitions/include/ospfv3/border-routers.xml.i
@@ -5,7 +5,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<tagNode name="border-routers">
diff --git a/op-mode-definitions/include/ospfv3/database.xml.i b/op-mode-definitions/include/ospfv3/database.xml.i
index e98f9e35b..fdc45f184 100644
--- a/op-mode-definitions/include/ospfv3/database.xml.i
+++ b/op-mode-definitions/include/ospfv3/database.xml.i
@@ -29,7 +29,7 @@
</completionHelp>
</properties>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
</children>
@@ -45,7 +45,7 @@
</properties>
<command>vtysh -c "show ipv6 ospf6 database * $6"</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/adv-router-id-node-tag.xml.i>
@@ -67,12 +67,12 @@
</properties>
<command>vtysh -c "show ipv6 ospf6 database as-external * $7"</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
</children>
</tagNode>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -87,14 +87,14 @@
</completionHelp>
</properties>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/self-originated.xml.i>
#include <include/ospfv3/adv-router-id-node-tag.xml.i>
</children>
</tagNode>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
#include <include/ospfv3/self-originated.xml.i>
@@ -105,7 +105,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -120,7 +120,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -135,7 +135,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -150,7 +150,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -165,7 +165,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -180,7 +180,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -195,7 +195,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -210,7 +210,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
@@ -225,7 +225,7 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/linkstate-id.xml.i>
diff --git a/op-mode-definitions/include/ospfv3/interface.xml.i b/op-mode-definitions/include/ospfv3/interface.xml.i
index 7a0b8ea48..45d5dbd45 100644
--- a/op-mode-definitions/include/ospfv3/interface.xml.i
+++ b/op-mode-definitions/include/ospfv3/interface.xml.i
@@ -11,7 +11,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<tagNode name="prefix">
@@ -23,7 +23,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
<node name="match">
<properties>
<help>Matched interface prefix information</help>
@@ -49,7 +49,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<tagNode name="prefix">
@@ -61,7 +61,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
<node name="match">
<properties>
<help>Matched interface prefix information</help>
diff --git a/op-mode-definitions/include/ospfv3/linkstate-id-node-tag.xml.i b/op-mode-definitions/include/ospfv3/linkstate-id-node-tag.xml.i
index ee3863b35..66674e754 100644
--- a/op-mode-definitions/include/ospfv3/linkstate-id-node-tag.xml.i
+++ b/op-mode-definitions/include/ospfv3/linkstate-id-node-tag.xml.i
@@ -8,7 +8,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
#include <include/ospfv3/self-originated.xml.i>
diff --git a/op-mode-definitions/include/ospfv3/linkstate-id.xml.i b/op-mode-definitions/include/ospfv3/linkstate-id.xml.i
index 9ead17c20..aa226c988 100644
--- a/op-mode-definitions/include/ospfv3/linkstate-id.xml.i
+++ b/op-mode-definitions/include/ospfv3/linkstate-id.xml.i
@@ -7,7 +7,7 @@
</completionHelp>
</properties>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
</children>
diff --git a/op-mode-definitions/include/ospfv3/linkstate.xml.i b/op-mode-definitions/include/ospfv3/linkstate.xml.i
index 78ef3efa1..030dc7923 100644
--- a/op-mode-definitions/include/ospfv3/linkstate.xml.i
+++ b/op-mode-definitions/include/ospfv3/linkstate.xml.i
@@ -4,7 +4,7 @@
<help>Show OSPFv3 linkstate routing information</help>
</properties>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
<tagNode name="network">
<properties>
<help>Show linkstate Network information</help>
diff --git a/op-mode-definitions/include/ospfv3/neighbor.xml.i b/op-mode-definitions/include/ospfv3/neighbor.xml.i
index 37859f815..b736270be 100644
--- a/op-mode-definitions/include/ospfv3/neighbor.xml.i
+++ b/op-mode-definitions/include/ospfv3/neighbor.xml.i
@@ -5,7 +5,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
<node name="drchoice">
<properties>
<help>Show neighbor DR choice information</help>
diff --git a/op-mode-definitions/include/ospfv3/route.xml.i b/op-mode-definitions/include/ospfv3/route.xml.i
index 9271c9c3a..a5b97cd05 100644
--- a/op-mode-definitions/include/ospfv3/route.xml.i
+++ b/op-mode-definitions/include/ospfv3/route.xml.i
@@ -11,7 +11,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<node name="external-2">
@@ -20,7 +20,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<node name="inter-area">
@@ -29,7 +29,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
<node name="intra-area">
@@ -38,10 +38,10 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
<node name="summary">
<properties>
<help>Show route table summary</help>
@@ -71,7 +71,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
</children>
</node>
</children>
diff --git a/op-mode-definitions/include/ospfv3/self-originated.xml.i b/op-mode-definitions/include/ospfv3/self-originated.xml.i
index 734f3f8ad..7549ccb2c 100644
--- a/op-mode-definitions/include/ospfv3/self-originated.xml.i
+++ b/op-mode-definitions/include/ospfv3/self-originated.xml.i
@@ -5,7 +5,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospfv3/detail.xml.i>
+ #include <include/frr-detail.xml.i>
#include <include/ospfv3/dump.xml.i>
#include <include/ospfv3/internal.xml.i>
</children>
diff --git a/op-mode-definitions/monitor-bridge.xml.in b/op-mode-definitions/monitor-bridge.xml.in
index 712a924f1..a43fa6dd9 100644
--- a/op-mode-definitions/monitor-bridge.xml.in
+++ b/op-mode-definitions/monitor-bridge.xml.in
@@ -4,14 +4,14 @@
<children>
<node name="bridge">
<properties>
- <help>Monitoring bridge database generated objects and address changes</help>
+ <help>Monitor bridge database changes</help>
</properties>
<command>sudo bridge monitor all</command>
<children>
<node name="link">
<command>sudo bridge monitor link</command>
<properties>
- <help>Monitoring bridge database generated connection interface changes</help>
+ <help>Monitor bridge database generated connection interface changes</help>
</properties>
</node>
<node name="fdb">
diff --git a/op-mode-definitions/monitor-command.xml.in b/op-mode-definitions/monitor-command.xml.in
new file mode 100644
index 000000000..31c68f029
--- /dev/null
+++ b/op-mode-definitions/monitor-command.xml.in
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="monitor">
+ <children>
+ <tagNode name="command">
+ <properties>
+ <help>Monitor operational mode command (refreshes every 2 seconds)</help>
+ </properties>
+ <command>watch --no-title ${vyos_op_scripts_dir}/vyos-op-cmd-wrapper.sh ${@:3}</command>
+ </tagNode>
+ <node name="command">
+ <children>
+ <node name="diff">
+ <properties>
+ <help>Show differences during each run</help>
+ </properties>
+ </node>
+ <tagNode name="diff">
+ <properties>
+ <help>Monitor operational mode command (refreshes every 2 seconds)</help>
+ </properties>
+ <command>watch --no-title --differences ${vyos_op_scripts_dir}/vyos-op-cmd-wrapper.sh ${@:4}</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index 06b1cf129..52b5b85d4 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -1,22 +1,25 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="monitor">
+ <properties>
+ <help>Monitor system information</help>
+ </properties>
<children>
<node name="log">
<properties>
<help>Monitor last lines of messages file</help>
</properties>
- <command>journalctl --no-hostname --follow --boot</command>
+ <command>SYSTEMD_LOG_COLOR=false journalctl --no-hostname --follow --boot</command>
<children>
<node name="color">
<properties>
<help>Output log in a colored fashion</help>
</properties>
- <command>grc journalctl --no-hostname --follow --boot</command>
+ <command>SYSTEMD_LOG_COLOR=false grc journalctl --no-hostname --follow --boot</command>
</node>
<node name="ids">
<properties>
- <help>Monitor log for Intrusion Detection System</help>
+ <help>Monitor Intrusion Detection System log</help>
</properties>
<children>
<leafNode name="ddos-protection">
@@ -27,20 +30,32 @@
</leafNode>
</children>
</node>
+ <leafNode name="conntrack-sync">
+ <properties>
+ <help>Monitor last lines of conntrack-sync log</help>
+ </properties>
+ <command>journalctl --no-hostname --follow --boot --unit conntrackd.service</command>
+ </leafNode>
+ <leafNode name="console-server">
+ <properties>
+ <help>Monitor last lines of console server log</help>
+ </properties>
+ <command>journalctl --no-hostname --follow --boot --unit conserver-server.service</command>
+ </leafNode>
<node name="dhcp">
<properties>
- <help>Monitor last lines of Dynamic Host Control Protocol (DHCP)</help>
+ <help>Monitor last lines of Dynamic Host Control Protocol log</help>
</properties>
<children>
<node name="server">
<properties>
- <help>Monitor last lines of DHCP server</help>
+ <help>Monitor last lines of DHCP server log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit isc-dhcp-server.service</command>
</node>
<node name="client">
<properties>
- <help>Monitor last lines of DHCP client</help>
+ <help>Monitor last lines of DHCP client log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit "dhclient@*.service"</command>
<children>
@@ -59,18 +74,18 @@
</node>
<node name="dhcpv6">
<properties>
- <help>Monitor last lines of Dynamic Host Control Protocol IPv6 (DHCPv6)</help>
+ <help>Monitor last lines of Dynamic Host Control Protocol IPv6 log</help>
</properties>
<children>
<node name="server">
<properties>
- <help>Monitor last lines of DHCPv6 server</help>
+ <help>Monitor last lines of DHCPv6 server log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit isc-dhcp-server6.service</command>
</node>
<node name="client">
<properties>
- <help>Monitor last lines of DHCPv6 client</help>
+ <help>Monitor last lines of DHCPv6 client log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit "dhcp6c@*.service"</command>
<children>
@@ -95,7 +110,7 @@
</leafNode>
<leafNode name="ipoe-server">
<properties>
- <help>Monitor last lines of IPoE server log</help>
+ <help>Monitor last lines of IP over Ethernet server log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit accel-ppp@ipoe.service</command>
</leafNode>
@@ -107,16 +122,33 @@
</leafNode>
<leafNode name="nhrp">
<properties>
- <help>Monitor last lines of Next Hop Resolution Protocol (NHRP) log</help>
+ <help>Monitor last lines of Next Hop Resolution Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit opennhrp.service</command>
</leafNode>
<leafNode name="ntp">
<properties>
- <help>Monitor last lines of Network Time Protocol (NTP) log</help>
+ <help>Monitor last lines of Network Time Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit chrony.service</command>
</leafNode>
+ <node name="openvpn">
+ <properties>
+ <help>Monitor last lines of OpenVPN log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit openvpn@*.service</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Monitor last lines of specific OpenVPN interface log</help>
+ <completionHelp>
+ <path>interfaces openvpn</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit openvpn@$5.service</command>
+ </tagNode>
+ </children>
+ </node>
<node name="pppoe">
<properties>
<help>Monitor last lines of PPPoE interface log</help>
@@ -142,7 +174,7 @@
</leafNode>
<node name="protocol">
<properties>
- <help>Monitor log for Routing Protocol</help>
+ <help>Monitor routing protocol logs</help>
</properties>
<children>
<leafNode name="ospf">
@@ -232,25 +264,61 @@
</node>
<leafNode name="router-advert">
<properties>
- <help>Monitor last lines of Router Advertisement Daemon (radvd)</help>
+ <help>Monitor last lines of Router Advertisement Daemon log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit radvd.service</command>
</leafNode>
<leafNode name="snmp">
<properties>
- <help>Monitor last lines of Simple Network Monitoring Protocol (SNMP)</help>
+ <help>Monitor last lines of Simple Network Monitoring Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit snmpd.service</command>
</leafNode>
<leafNode name="ssh">
<properties>
- <help>Monitor last lines of Secure Shell (SSH)</help>
+ <help>Monitor last lines of Secure Shell log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit ssh.service</command>
</leafNode>
+ <leafNode name="vpn">
+ <properties>
+ <help>Monitor last lines of ALL Virtual Private Network services</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command>
+ </leafNode>
+ <leafNode name="ipsec">
+ <properties>
+ <help>Monitor last lines of IPsec log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit strongswan.service</command>
+ </leafNode>
+ <leafNode name="l2tp">
+ <properties>
+ <help>Monitor last lines of L2TP log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit accel-ppp@l2tp.service</command>
+ </leafNode>
+ <leafNode name="openconnect">
+ <properties>
+ <help>Monitor last lines of OpenConnect log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit ocserv.service</command>
+ </leafNode>
+ <leafNode name="pptp">
+ <properties>
+ <help>Monitor last lines of PPTP log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit accel-ppp@pptp.service</command>
+ </leafNode>
+ <leafNode name="sstp">
+ <properties>
+ <help>Monitor last lines of Secure Socket Tunneling Protocol server</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit accel-ppp@sstp.service</command>
+ </leafNode>
<node name="sstpc">
<properties>
- <help>Monitor last lines of SSTP client log</help>
+ <help>Monitor last lines of Secure Socket Tunneling Protocol client</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit "ppp@sstpc*.service"</command>
<children>
@@ -265,49 +333,18 @@
</tagNode>
</children>
</node>
- <node name="vpn">
+ <leafNode name="vpp">
<properties>
- <help>Monitor Virtual Private Network (VPN) services</help>
+ <help>Monitor last lines of Vector Packet Processor log</help>
</properties>
- <children>
- <leafNode name="all">
- <properties>
- <help>Monitor last lines of ALL VPNs</help>
- </properties>
- <command>journalctl --no-hostname --boot --follow --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command>
- </leafNode>
- <leafNode name="ipsec">
- <properties>
- <help>Monitor last lines of IPsec</help>
- </properties>
- <command>journalctl --no-hostname --boot --follow --unit strongswan.service</command>
- </leafNode>
- <leafNode name="l2tp">
- <properties>
- <help>Monitor last lines of L2TP</help>
- </properties>
- <command>journalctl --no-hostname --boot --follow --unit accel-ppp@l2tp.service</command>
- </leafNode>
- <leafNode name="openconnect">
- <properties>
- <help>Monitor last lines of OpenConnect</help>
- </properties>
- <command>journalctl --no-hostname --boot --follow --unit ocserv.service</command>
- </leafNode>
- <leafNode name="pptp">
- <properties>
- <help>Monitor last lines of PPTP</help>
- </properties>
- <command>journalctl --no-hostname --boot --follow --unit accel-ppp@pptp.service</command>
- </leafNode>
- <leafNode name="sstp">
- <properties>
- <help>Monitor last lines of SSTP</help>
- </properties>
- <command>journalctl --no-hostname --boot --follow --unit accel-ppp@sstp.service</command>
- </leafNode>
- </children>
- </node>
+ <command>journalctl --no-hostname --boot --follow --unit vpp.service</command>
+ </leafNode>
+ <leafNode name="vrrp">
+ <properties>
+ <help>Monitor last lines of Virtual Router Redundancy Protocol log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --follow --unit keepalived.service</command>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/monitor-ndp.xml.in b/op-mode-definitions/monitor-ndp.xml.in
index 26d881f1a..3b08f3d73 100644
--- a/op-mode-definitions/monitor-ndp.xml.in
+++ b/op-mode-definitions/monitor-ndp.xml.in
@@ -4,14 +4,14 @@
<children>
<node name="ndp">
<properties>
- <help>Monitor the NDP information received by the router through the device</help>
+ <help>Monitor Neighbor Discovery Protocol (NDP) information</help>
</properties>
<command>sudo ndptool monitor</command>
<children>
<tagNode name="interface">
<command>sudo ndptool monitor --ifname=$4</command>
<properties>
- <help>Monitor ndp protocol on specified interface</help>
+ <help>Monitor Neighbor Discovery Protocol on specified interface</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
@@ -20,7 +20,7 @@
<tagNode name="type">
<command>sudo ndptool monitor --ifname=$4 --msg-type=$6</command>
<properties>
- <help>Monitor specific types of NDP protocols</help>
+ <help>Monitor specific Neighbor Discovery Protocol type</help>
<completionHelp>
<list>rs ra ns na</list>
</completionHelp>
@@ -31,7 +31,7 @@
<tagNode name="type">
<command>sudo ndptool monitor --msg-type=$4</command>
<properties>
- <help>Monitor specific types of NDP protocols</help>
+ <help>Monitor specific Neighbor Discovery Protocol type</help>
<completionHelp>
<list>rs ra ns na</list>
</completionHelp>
diff --git a/op-mode-definitions/monitor-protocol.xml.in b/op-mode-definitions/monitor-protocol.xml.in
index f3af3575c..f05a1945f 100644
--- a/op-mode-definitions/monitor-protocol.xml.in
+++ b/op-mode-definitions/monitor-protocol.xml.in
@@ -12,7 +12,6 @@
<help>Monitor the Border Gateway Protocol (BGP)</help>
</properties>
<children>
- #include <include/monitor-background.xml.i>
<node name="disable">
<properties>
<help>Disable Border Gateway Protocol (BGP) debugging</help>
@@ -266,7 +265,6 @@
<help>Monitor Open Shortest Path First (OSPF) protocol</help>
</properties>
<children>
- #include <include/monitor-background.xml.i>
<node name="disable">
<properties>
<help>Disable Open Shortest Path First (OSPF) debugging</help>
@@ -650,25 +648,6 @@
<help>Monitor the IPv6 Open Shortest Path First (OSPFv3) protocol</help>
</properties>
<children>
- <node name="background">
- <properties>
- <help>Monitor in background</help>
- </properties>
- <children>
- <node name="start">
- <properties>
- <help>Start background monitoring</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor-background OSPFv3 ospf6</command>
- </node>
- <node name="stop">
- <properties>
- <help>Stop background monitoring</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor-background-stop OSPFv3</command>
- </node>
- </children>
- </node>
<node name="disable">
<properties>
<help>Disable IPv6 Open Shortest Path First (OSPFv3) protocol debugging</help>
@@ -1192,25 +1171,6 @@
<help>Monitor the Routing Information Base (RIB)</help>
</properties>
<children>
- <node name="background">
- <properties>
- <help>Monitor in background</help>
- </properties>
- <children>
- <node name="start">
- <properties>
- <help>Start background monitoring</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor-background RIB zebra</command>
- </node>
- <node name="stop">
- <properties>
- <help>Stop background monitoring</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor-background-stop RIB</command>
- </node>
- </children>
- </node>
<node name="disable">
<properties>
<help>Disable Route Information Base (RIB) debugging</help>
@@ -1360,7 +1320,6 @@
<help>Monitor the Routing Information Protocol (RIP)</help>
</properties>
<children>
- #include <include/monitor-background.xml.i>
<node name="disable">
<properties>
<help>Disable Routing Information Protocol (RIP) debugging</help>
@@ -1452,7 +1411,6 @@
<help>Monitor the Routing Information Protocol Next Generation (RIPng) protocol</help>
</properties>
<children>
- #include <include/monitor-background.xml.i>
<node name="disable">
<properties>
<help>Disable Routing Information Protocol Next Generation (RIPNG) debugging</help>
diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in
index 346febec0..ca0eb3687 100644
--- a/op-mode-definitions/pki.xml.in
+++ b/op-mode-definitions/pki.xml.in
@@ -505,6 +505,14 @@
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4"</command>
+ <children>
+ <leafNode name="pem">
+ <properties>
+ <help>Show x509 CA certificate in PEM format</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4" --pem</command>
+ </leafNode>
+ </children>
</tagNode>
<leafNode name="certificate">
<properties>
@@ -520,6 +528,23 @@
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4"</command>
+ <children>
+ <leafNode name="pem">
+ <properties>
+ <help>Show x509 certificate in PEM format</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" --pem</command>
+ </leafNode>
+ <tagNode name="fingerprint">
+ <properties>
+ <help>Show x509 certificate fingerprint</help>
+ <completionHelp>
+ <list>sha256 sha384 sha512</list>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" --fingerprint "$6"</command>
+ </tagNode>
+ </children>
</tagNode>
<leafNode name="crl">
<properties>
@@ -527,6 +552,23 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "all"</command>
</leafNode>
+ <tagNode name="crl">
+ <properties>
+ <help>Show x509 certificate revocation lists by CA name</help>
+ <completionHelp>
+ <path>pki ca</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$4"</command>
+ <children>
+ <leafNode name="pem">
+ <properties>
+ <help>Show x509 certificate revocation lists by CA name in PEM format</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$4" --pem</command>
+ </leafNode>
+ </children>
+ </tagNode>
</children>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show</command>
</node>
diff --git a/op-mode-definitions/show-rpki.xml.in b/op-mode-definitions/rpki.xml.in
index c1902ccec..72d378b88 100644
--- a/op-mode-definitions/show-rpki.xml.in
+++ b/op-mode-definitions/rpki.xml.in
@@ -29,4 +29,14 @@
</node>
</children>
</node>
+ <node name="reset">
+ <children>
+ <leafNode name="rpki">
+ <properties>
+ <help>Reset RPKI</help>
+ </properties>
+ <command>vtysh -c "rpki reset"</command>
+ </leafNode>
+ </children>
+ </node>
</interfaceDefinition>
diff --git a/op-mode-definitions/show-bridge.xml.in b/op-mode-definitions/show-bridge.xml.in
index acf3a00c7..fad3f3418 100644
--- a/op-mode-definitions/show-bridge.xml.in
+++ b/op-mode-definitions/show-bridge.xml.in
@@ -7,12 +7,20 @@
<help>Show bridging information</help>
</properties>
<children>
- <leafNode name="vlan">
+ <node name="vlan">
<properties>
<help>View the VLAN filter settings of the bridge</help>
</properties>
<command>${vyos_op_scripts_dir}/bridge.py show_vlan</command>
- </leafNode>
+ <children>
+ <leafNode name="tunnel">
+ <properties>
+ <help>Show bridge VLAN tunnel mapping</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/bridge.py show_vlan --tunnel</command>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
<leafNode name="bridge">
@@ -42,6 +50,18 @@
</properties>
<command>${vyos_op_scripts_dir}/bridge.py show_fdb --interface=$3</command>
</leafNode>
+ <leafNode name="detail">
+ <properties>
+ <help>Display bridge interface details</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/bridge.py show_detail --interface=$3</command>
+ </leafNode>
+ <leafNode name="nexthop-group">
+ <properties>
+ <help>Display bridge interface nexthop-group</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/bridge.py show_detail --nexthop_group --interface=$3</command>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/op-mode-definitions/show-evpn.xml.in b/op-mode-definitions/show-evpn.xml.in
index 0bdb41e7a..a005cbc30 100644
--- a/op-mode-definitions/show-evpn.xml.in
+++ b/op-mode-definitions/show-evpn.xml.in
@@ -7,6 +7,34 @@
<help>Show Ethernet VPN (EVPN) information</help>
</properties>
<children>
+ <node name="access-vlan">
+ <properties>
+ <help>Access VLANs</help>
+ </properties>
+ <children>
+ #include <include/frr-detail.xml.i>
+ </children>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ <tagNode name="access-vlan">
+ <properties>
+ <help>Access VLANs interface name</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces --bridgeable --no-vlan-subinterfaces</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="node.tag">
+ <properties>
+ <help>VLAN ID</help>
+ <completionHelp>
+ <list>&lt;1-4094&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ </children>
+ </tagNode>
<node name="arp-cache">
<properties>
<help>ARP and ND cache</help>
diff --git a/op-mode-definitions/show-interfaces-bonding.xml.in b/op-mode-definitions/show-interfaces-bonding.xml.in
index aa224e6cf..8ca5adb4f 100644
--- a/op-mode-definitions/show-interfaces-bonding.xml.in
+++ b/op-mode-definitions/show-interfaces-bonding.xml.in
@@ -48,12 +48,6 @@
</leafNode>
</children>
</tagNode>
- <leafNode name="xdp">
- <properties>
- <help>Show eXpress Data Path statistics</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/show_xdp_stats.sh bonding "$4"</command>
- </leafNode>
</children>
</tagNode>
<node name="bonding">
diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in
index 7c12d6084..09f0b3933 100644
--- a/op-mode-definitions/show-interfaces-ethernet.xml.in
+++ b/op-mode-definitions/show-interfaces-ethernet.xml.in
@@ -68,12 +68,6 @@
</leafNode>
</children>
</tagNode>
- <leafNode name="xdp">
- <properties>
- <help>Show eXpress Data Path statistics</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/show_xdp_stats.sh ethernet "$4"</command>
- </leafNode>
</children>
</tagNode>
<node name="ethernet">
diff --git a/op-mode-definitions/show-ip-ospf.xml.in b/op-mode-definitions/show-ip-ospf.xml.in
index 704ed984f..f3b9da90c 100644
--- a/op-mode-definitions/show-ip-ospf.xml.in
+++ b/op-mode-definitions/show-ip-ospf.xml.in
@@ -13,7 +13,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospf-common.xml.i>
+ #include <include/ospf/common.xml.i>
<tagNode name="vrf">
<properties>
<help>Show OSPF routing protocol for given VRF</help>
@@ -24,7 +24,7 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- #include <include/ospf-common.xml.i>
+ #include <include/ospf/common.xml.i>
</children>
</tagNode>
</children>
diff --git a/op-mode-definitions/show-ipv6-ospfv3.xml.in b/op-mode-definitions/show-ipv6-ospfv3.xml.in
index a63465472..e1fcf470f 100644
--- a/op-mode-definitions/show-ipv6-ospfv3.xml.in
+++ b/op-mode-definitions/show-ipv6-ospfv3.xml.in
@@ -41,6 +41,7 @@
</tagNode>
#include <include/ospfv3/border-routers.xml.i>
#include <include/ospfv3/database.xml.i>
+ #include <include/ospf/graceful-restart.xml.i>
#include <include/ospfv3/interface.xml.i>
#include <include/ospfv3/linkstate.xml.i>
#include <include/ospfv3/neighbor.xml.i>
@@ -94,6 +95,7 @@
</tagNode>
#include <include/ospfv3/border-routers.xml.i>
#include <include/ospfv3/database.xml.i>
+ #include <include/ospf/graceful-restart.xml.i>
#include <include/ospfv3/interface.xml.i>
#include <include/ospfv3/linkstate.xml.i>
#include <include/ospfv3/neighbor.xml.i>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 7663e4c00..747622db6 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -1,10 +1,22 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="show">
+ <properties>
+ <help>Show system information</help>
+ </properties>
<children>
+ <tagNode name="log">
+ <properties>
+ <help>Show last number of messages in master logging buffer</help>
+ <completionHelp>
+ <list>&lt;1-9999&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>if ${vyos_validators_dir}/numeric --range 1-9999 "$3"; then journalctl --no-hostname --boot --lines "$3"; fi</command>
+ </tagNode>
<node name="log">
<properties>
- <help>Show contents of current master log file</help>
+ <help>Show contents of current master logging buffer</help>
</properties>
<command>journalctl --no-hostname --boot</command>
<children>
@@ -38,6 +50,12 @@
</properties>
<command>journalctl --no-hostname --boot --unit conntrackd.service</command>
</leafNode>
+ <leafNode name="console-server">
+ <properties>
+ <help>Show log for console server</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit conserver-server.service</command>
+ </leafNode>
<node name="ids">
<properties>
<help>Show log for for Intrusion Detection System</help>
@@ -386,23 +404,6 @@
</properties>
<command>journalctl --no-hostname --boot --unit ssh.service</command>
</leafNode>
- <node name="sstpc">
- <properties>
- <help>Show log for SSTP client</help>
- </properties>
- <command>journalctl --no-hostname --boot --unit "ppp@sstpc*.service"</command>
- <children>
- <tagNode name="interface">
- <properties>
- <help>Show SSTP client log on specific interface</help>
- <completionHelp>
- <path>interfaces sstpc</path>
- </completionHelp>
- </properties>
- <command>journalctl --no-hostname --boot --unit "ppp@$5.service"</command>
- </tagNode>
- </children>
- </node>
<tagNode name="tail">
<properties>
<help>Show last n changes to messages</help>
@@ -418,49 +419,65 @@
</properties>
<command>tail -n 10 /var/log/messages</command>
</node>
- <node name="vpn">
+ <leafNode name="vpn">
+ <properties>
+ <help>Monitor last lines of ALL Virtual Private Network services</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command>
+ </leafNode>
+ <leafNode name="ipsec">
+ <properties>
+ <help>Show log for IPsec</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit strongswan.service</command>
+ </leafNode>
+ <leafNode name="l2tp">
+ <properties>
+ <help>Show log for L2TP</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit accel-ppp@l2tp.service</command>
+ </leafNode>
+ <leafNode name="openconnect">
+ <properties>
+ <help>Show log for OpenConnect</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit ocserv.service</command>
+ </leafNode>
+ <leafNode name="pptp">
+ <properties>
+ <help>Show log for PPTP</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit accel-ppp@pptp.service</command>
+ </leafNode>
+ <leafNode name="sstp">
<properties>
- <help>Show log for Virtual Private Network (VPN)</help>
+ <help>Show log for Secure Socket Tunneling Protocol (SSTP) server</help>
</properties>
+ <command>journalctl --no-hostname --boot --unit accel-ppp@sstp.service</command>
+ </leafNode>
+ <node name="sstpc">
+ <properties>
+ <help>Show log for Secure Socket Tunneling Protocol (SSTP) client</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit "ppp@sstpc*.service"</command>
<children>
- <leafNode name="all">
- <properties>
- <help>Show log for ALL</help>
- </properties>
- <command>journalctl --no-hostname --boot --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command>
- </leafNode>
- <leafNode name="ipsec">
- <properties>
- <help>Show log for IPsec</help>
- </properties>
- <command>journalctl --no-hostname --boot --unit strongswan.service</command>
- </leafNode>
- <leafNode name="l2tp">
- <properties>
- <help>Show log for L2TP</help>
- </properties>
- <command>journalctl --no-hostname --boot --unit accel-ppp@l2tp.service</command>
- </leafNode>
- <leafNode name="openconnect">
- <properties>
- <help>Show log for OpenConnect</help>
- </properties>
- <command>journalctl --no-hostname --boot --unit ocserv.service</command>
- </leafNode>
- <leafNode name="pptp">
- <properties>
- <help>Show log for PPTP</help>
- </properties>
- <command>journalctl --no-hostname --boot --unit accel-ppp@pptp.service</command>
- </leafNode>
- <leafNode name="sstp">
+ <tagNode name="interface">
<properties>
- <help>Show log for SSTP</help>
+ <help>Show SSTP client log on specific interface</help>
+ <completionHelp>
+ <path>interfaces sstpc</path>
+ </completionHelp>
</properties>
- <command>journalctl --no-hostname --boot --unit accel-ppp@sstp.service</command>
- </leafNode>
+ <command>journalctl --no-hostname --boot --unit "ppp@$5.service"</command>
+ </tagNode>
</children>
</node>
+ <leafNode name="vpp">
+ <properties>
+ <help>Show log for Vector Packet Processor (VPP)</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit vpp.service</command>
+ </leafNode>
<leafNode name="vrrp">
<properties>
<help>Show log for Virtual Router Redundancy Protocol (VRRP)</help>
diff --git a/op-mode-definitions/show-reverse-proxy.xml.in b/op-mode-definitions/show-reverse-proxy.xml.in
new file mode 100644
index 000000000..ed0fee843
--- /dev/null
+++ b/op-mode-definitions/show-reverse-proxy.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="reverse-proxy">
+ <properties>
+ <help>Show load-balancing reverse-proxy</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/reverseproxy.py show</command>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/webproxy.xml.in b/op-mode-definitions/webproxy.xml.in
index 5ae1577d8..57df44ff8 100644
--- a/op-mode-definitions/webproxy.xml.in
+++ b/op-mode-definitions/webproxy.xml.in
@@ -2,43 +2,28 @@
<interfaceDefinition>
<node name="monitor">
<children>
- <node name="webproxy">
- <properties>
- <help>Monitor WebProxy service</help>
- </properties>
- <command>${vyatta_bindir}/vyatta-monitor Webproxy squid</command>
+ <node name="log">
<children>
- <node name="access-log">
- <properties>
- <help>Monitor the last lines of the squid access log</help>
- </properties>
- <command>if [ -f /var/log/squid/access.log ]; then sudo tail --follow=name /var/log/squid/access.log; else echo "WebProxy access-log does not exist"; fi</command>
- </node>
- <node name="background">
+ <node name="webproxy">
<properties>
- <help>Monitor Webproxy in the background</help>
+ <help>Monitor last lines of Webproxy log</help>
</properties>
+ <command>journalctl --no-hostname --boot --follow --unit squid.service</command>
<children>
- <node name="start">
+ <leafNode name="access-log">
<properties>
- <help>Start background monitoring of Webproxy</help>
+ <help>Monitor the last lines of the Webproxy access log</help>
</properties>
- <command>${vyatta_bindir}/vyatta-monitor-background Webproxy squid</command>
- </node>
- <node name="stop">
+ <command>if [ -f /var/log/squid/access.log ]; then sudo tail --follow=name /var/log/squid/access.log; else echo "WebProxy access-log does not exist"; fi</command>
+ </leafNode>
+ <leafNode name="cache-log">
<properties>
- <help>Stop background monitoring of Webproxy</help>
+ <help>Monitor the last lines of the Webproxy cache log</help>
</properties>
- <command>${vyatta_bindir}/vyatta-monitor-background-stop Webproxy </command>
- </node>
+ <command>if [ -f /var/log/squid/cache.log ]; then sudo tail --follow=name /var/log/squid/cache.log; else echo "WebProxy cache-log does not exist"; fi</command>
+ </leafNode>
</children>
</node>
- <node name="cache-log">
- <properties>
- <help>Monitor the last lines of the squid cache log</help>
- </properties>
- <command>if [ -f /var/log/squid/cache.log ]; then sudo tail --follow=name /var/log/squid/cache.log; else echo "WebProxy cache-log does not exist"; fi</command>
- </node>
</children>
</node>
</children>
@@ -102,6 +87,17 @@
<help>Update the webproxy blacklist database</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/webproxy_update_blacklist.sh --update-blacklist</command>
+ <children>
+ <tagNode name="vrf">
+ <properties>
+ <help>Update webproxy blacklist database via specified VRF</help>
+ <completionHelp>
+ <path>vrf name</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/webproxy_update_blacklist.sh --update-blacklist --vrf "${5}" </command>
+ </tagNode>
+ </children>
</node>
</children>
</node>
diff --git a/python/vyos/accel_ppp.py b/python/vyos/accel_ppp.py
index 0af311e57..0b4f8a9fe 100644
--- a/python/vyos/accel_ppp.py
+++ b/python/vyos/accel_ppp.py
@@ -18,7 +18,7 @@
import sys
import vyos.opmode
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
def get_server_statistics(accel_statistics, pattern, sep=':') -> dict:
diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py
index a4e318d08..84e0ae51a 100644
--- a/python/vyos/component_version.py
+++ b/python/vyos/component_version.py
@@ -37,7 +37,7 @@ import re
import sys
import fileinput
-from vyos.xml import component_version
+from vyos.xml_ref import component_version
from vyos.version import get_version
from vyos.defaults import directories
diff --git a/python/vyos/config.py b/python/vyos/config.py
index 287fd2ed1..0ca41718f 100644
--- a/python/vyos/config.py
+++ b/python/vyos/config.py
@@ -1,4 +1,4 @@
-# Copyright 2017, 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2017, 2019-2023 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
@@ -66,11 +66,31 @@ In operational mode, all functions return values from the running config.
import re
import json
from copy import deepcopy
+from typing import Union
-import vyos.xml
-import vyos.util
import vyos.configtree
-from vyos.configsource import ConfigSource, ConfigSourceSession
+from vyos.xml_ref import multi_to_list
+from vyos.xml_ref import from_source
+from vyos.xml_ref import ext_dict_merge
+from vyos.xml_ref import relative_defaults
+from vyos.utils.dict import get_sub_dict
+from vyos.utils.dict import mangle_dict_keys
+from vyos.configsource import ConfigSource
+from vyos.configsource import ConfigSourceSession
+
+class ConfigDict(dict):
+ _from_defaults = {}
+ _dict_kwargs = {}
+ def from_defaults(self, path: list[str]) -> bool:
+ return from_source(self._from_defaults, path)
+ @property
+ def kwargs(self) -> dict:
+ return self._dict_kwargs
+
+def config_dict_merge(src: dict, dest: Union[dict, ConfigDict]) -> ConfigDict:
+ if not isinstance(dest, ConfigDict):
+ dest = ConfigDict(dest)
+ return ext_dict_merge(src, dest)
class Config(object):
"""
@@ -93,6 +113,11 @@ class Config(object):
(self._running_config,
self._session_config) = self._config_source.get_configtree_tuple()
+ def get_config_tree(self, effective=False):
+ if effective:
+ return self._running_config
+ return self._session_config
+
def _make_path(self, path):
# Backwards-compatibility stuff: original implementation used string paths
# libvyosconfig paths are lists, but since node names cannot contain whitespace,
@@ -223,9 +248,17 @@ class Config(object):
return config_dict
+ def verify_mangling(self, key_mangling):
+ if not (isinstance(key_mangling, tuple) and \
+ (len(key_mangling) == 2) and \
+ isinstance(key_mangling[0], str) and \
+ isinstance(key_mangling[1], str)):
+ raise ValueError("key_mangling must be a tuple of two strings")
+
def get_config_dict(self, path=[], effective=False, key_mangling=None,
get_first_key=False, no_multi_convert=False,
- no_tag_node_value_mangle=False):
+ no_tag_node_value_mangle=False,
+ with_defaults=False, with_recursive_defaults=False):
"""
Args:
path (str list): Configuration tree path, can be empty
@@ -236,32 +269,73 @@ class Config(object):
Returns: a dict representation of the config under path
"""
+ kwargs = locals().copy()
+ del kwargs['self']
+ del kwargs['no_multi_convert']
+ del kwargs['with_defaults']
+ del kwargs['with_recursive_defaults']
+
lpath = self._make_path(path)
root_dict = self.get_cached_root_dict(effective)
- conf_dict = vyos.util.get_sub_dict(root_dict, lpath, get_first_key)
-
- if not key_mangling and no_multi_convert:
- return deepcopy(conf_dict)
+ conf_dict = get_sub_dict(root_dict, lpath, get_first_key=get_first_key)
- xmlpath = lpath if get_first_key else lpath[:-1]
+ rpath = lpath if get_first_key else lpath[:-1]
- if not key_mangling:
- conf_dict = vyos.xml.multi_to_list(xmlpath, conf_dict)
- return conf_dict
+ if not no_multi_convert:
+ conf_dict = multi_to_list(rpath, conf_dict)
- if no_multi_convert is False:
- conf_dict = vyos.xml.multi_to_list(xmlpath, conf_dict)
+ if key_mangling is not None:
+ self.verify_mangling(key_mangling)
+ conf_dict = mangle_dict_keys(conf_dict,
+ key_mangling[0], key_mangling[1],
+ abs_path=rpath,
+ no_tag_node_value_mangle=no_tag_node_value_mangle)
- if not (isinstance(key_mangling, tuple) and \
- (len(key_mangling) == 2) and \
- isinstance(key_mangling[0], str) and \
- isinstance(key_mangling[1], str)):
- raise ValueError("key_mangling must be a tuple of two strings")
+ if with_defaults or with_recursive_defaults:
+ defaults = self.get_config_defaults(**kwargs,
+ recursive=with_recursive_defaults)
+ conf_dict = config_dict_merge(defaults, conf_dict)
+ else:
+ conf_dict = ConfigDict(conf_dict)
- conf_dict = vyos.util.mangle_dict_keys(conf_dict, key_mangling[0], key_mangling[1], abs_path=xmlpath, no_tag_node_value_mangle=no_tag_node_value_mangle)
+ # save optional args for a call to get_config_defaults
+ setattr(conf_dict, '_dict_kwargs', kwargs)
return conf_dict
+ def get_config_defaults(self, path=[], effective=False, key_mangling=None,
+ no_tag_node_value_mangle=False, get_first_key=False,
+ recursive=False) -> dict:
+ lpath = self._make_path(path)
+ root_dict = self.get_cached_root_dict(effective)
+ conf_dict = get_sub_dict(root_dict, lpath, get_first_key)
+
+ defaults = relative_defaults(lpath, conf_dict,
+ get_first_key=get_first_key,
+ recursive=recursive)
+
+ rpath = lpath if get_first_key else lpath[:-1]
+
+ if key_mangling is not None:
+ self.verify_mangling(key_mangling)
+ defaults = mangle_dict_keys(defaults,
+ key_mangling[0], key_mangling[1],
+ abs_path=rpath,
+ no_tag_node_value_mangle=no_tag_node_value_mangle)
+
+ return defaults
+
+ def merge_defaults(self, config_dict: ConfigDict, recursive=False):
+ if not isinstance(config_dict, ConfigDict):
+ raise TypeError('argument is not of type ConfigDict')
+ if not config_dict.kwargs:
+ raise ValueError('argument missing metadata')
+
+ args = config_dict.kwargs
+ d = self.get_config_defaults(**args, recursive=recursive)
+ config_dict = config_dict_merge(d, config_dict)
+ return config_dict
+
def is_multi(self, path):
"""
Args:
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index fade3081c..0fc72e660 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -18,17 +18,23 @@ import re
import sys
import gzip
import logging
+
from typing import Optional, Tuple, Union
from filecmp import cmp
from datetime import datetime
+from textwrap import dedent
+from pathlib import Path
from tabulate import tabulate
from vyos.config import Config
from vyos.configtree import ConfigTree, ConfigTreeError, show_diff
from vyos.defaults import directories
-from vyos.util import is_systemd_service_active, ask_yes_no, rc_cmd
+from vyos.version import get_full_version_data
+from vyos.utils.io import ask_yes_no
+from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import rc_cmd
-SAVE_CONFIG = '/opt/vyatta/sbin/vyatta-save-config.pl'
+SAVE_CONFIG = '/usr/libexec/vyos/vyos-save-config.py'
# created by vyatta-cfg-postinst
commit_post_hook_dir = '/etc/commit/post-hooks.d'
@@ -56,6 +62,44 @@ formatter = logging.Formatter('%(funcName)s: %(levelname)s:%(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
+def save_config(target):
+ cmd = f'{SAVE_CONFIG} {target}'
+ rc, out = rc_cmd(cmd)
+ if rc != 0:
+ logger.critical(f'save config failed: {out}')
+
+def unsaved_commits() -> bool:
+ if get_full_version_data()['boot_via'] == 'livecd':
+ return False
+ tmp_save = '/tmp/config.running'
+ save_config(tmp_save)
+ ret = not cmp(tmp_save, config_file, shallow=False)
+ os.unlink(tmp_save)
+ return ret
+
+def get_file_revision(rev: int):
+ revision = os.path.join(archive_dir, f'config.boot.{rev}.gz')
+ try:
+ with gzip.open(revision) as f:
+ r = f.read().decode()
+ except FileNotFoundError:
+ logger.warning(f'commit revision {rev} not available')
+ return ''
+ return r
+
+def get_config_tree_revision(rev: int):
+ c = get_file_revision(rev)
+ return ConfigTree(c)
+
+def is_node_revised(path: list = [], rev1: int = 1, rev2: int = 0) -> bool:
+ from vyos.configtree import DiffTree
+ left = get_config_tree_revision(rev1)
+ right = get_config_tree_revision(rev2)
+ diff_tree = DiffTree(left, right)
+ if diff_tree.add.exists(path) or diff_tree.sub.exists(path):
+ return True
+ return False
+
class ConfigMgmtError(Exception):
pass
@@ -98,20 +142,6 @@ class ConfigMgmt:
self.active_config = config._running_config
self.working_config = config._session_config
- @staticmethod
- def save_config(target):
- cmd = f'{SAVE_CONFIG} {target}'
- rc, out = rc_cmd(cmd)
- if rc != 0:
- logger.critical(f'save config failed: {out}')
-
- def _unsaved_commits(self) -> bool:
- tmp_save = '/tmp/config.boot.check-save'
- self.save_config(tmp_save)
- ret = not cmp(tmp_save, config_file, shallow=False)
- os.unlink(tmp_save)
- return ret
-
# Console script functions
#
def commit_confirm(self, minutes: int=DEFAULT_TIME_MINUTES,
@@ -123,7 +153,7 @@ class ConfigMgmt:
msg = 'Another confirm is pending'
return msg, 1
- if self._unsaved_commits():
+ if unsaved_commits():
W = '\nYou should save previous commits before commit-confirm !\n'
else:
W = ''
@@ -431,26 +461,25 @@ Proceed ?'''
return ConfigTree(c)
def _add_logrotate_conf(self):
- conf = f"""{archive_config_file} {{
- su root vyattacfg
- rotate {self.max_revisions}
- start 0
- compress
- copy
-}}"""
- mask = os.umask(0o133)
-
- with open(logrotate_conf, 'w') as f:
- f.write(conf)
-
- os.umask(mask)
+ conf: str = dedent(f"""\
+ {archive_config_file} {{
+ su root vyattacfg
+ rotate {self.max_revisions}
+ start 0
+ compress
+ copy
+ }}
+ """)
+ conf_file = Path(logrotate_conf)
+ conf_file.write_text(conf)
+ conf_file.chmod(0o644)
def _archive_active_config(self) -> bool:
mask = os.umask(0o113)
ext = os.getpid()
tmp_save = f'/tmp/config.boot.{ext}'
- self.save_config(tmp_save)
+ save_config(tmp_save)
try:
if cmp(tmp_save, archive_config_file, shallow=False):
diff --git a/python/vyos/configdep.py b/python/vyos/configdep.py
index d4b2cc78f..7a8559839 100644
--- a/python/vyos/configdep.py
+++ b/python/vyos/configdep.py
@@ -18,7 +18,7 @@ import json
import typing
from inspect import stack
-from vyos.util import load_as_module
+from vyos.utils.system import load_as_module
from vyos.defaults import directories
from vyos.configsource import VyOSError
from vyos import ConfigError
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 6ab5c252c..71a06b625 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -19,9 +19,8 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion.
import os
import json
-from vyos.util import dict_search
-from vyos.xml import defaults
-from vyos.util import cmd
+from vyos.utils.dict import dict_search
+from vyos.utils.process import cmd
def retrieve_config(path_hash, base_path, config):
"""
@@ -177,24 +176,6 @@ def get_removed_vlans(conf, path, dict):
return dict
-def T2665_set_dhcpv6pd_defaults(config_dict):
- """ Properly configure DHCPv6 default options in the dictionary. If there is
- no DHCPv6 configured at all, it is safe to remove the entire configuration.
- """
- # As this is the same for every interface type it is safe to assume this
- # for ethernet
- pd_defaults = defaults(['interfaces', 'ethernet', 'dhcpv6-options', 'pd'])
-
- # Implant default dictionary for DHCPv6-PD instances
- if dict_search('dhcpv6_options.pd.length', config_dict):
- del config_dict['dhcpv6_options']['pd']['length']
-
- for pd in (dict_search('dhcpv6_options.pd', config_dict) or []):
- config_dict['dhcpv6_options']['pd'][pd] = dict_merge(pd_defaults,
- config_dict['dhcpv6_options']['pd'][pd])
-
- return config_dict
-
def is_member(conf, interface, intftype=None):
"""
Checks if passed interface is member of other interface of specified type.
@@ -263,6 +244,48 @@ def is_mirror_intf(conf, interface, direction=None):
return ret_val
+def has_address_configured(conf, intf):
+ """
+ Checks if interface has an address configured.
+ Checks the following config nodes:
+ 'address', 'ipv6 address eui64', 'ipv6 address autoconf'
+
+ Returns True if interface has address configured, False if it doesn't.
+ """
+ from vyos.ifconfig import Section
+ ret = False
+
+ old_level = conf.get_level()
+ conf.set_level([])
+
+ intfpath = 'interfaces ' + Section.get_config_path(intf)
+ if ( conf.exists(f'{intfpath} address') or
+ conf.exists(f'{intfpath} ipv6 address autoconf') or
+ conf.exists(f'{intfpath} ipv6 address eui64') ):
+ ret = True
+
+ conf.set_level(old_level)
+ return ret
+
+def has_vrf_configured(conf, intf):
+ """
+ Checks if interface has a VRF configured.
+
+ Returns True if interface has VRF configured, False if it doesn't.
+ """
+ from vyos.ifconfig import Section
+ ret = False
+
+ old_level = conf.get_level()
+ conf.set_level([])
+
+ tmp = ['interfaces', Section.get_config_path(intf), 'vrf']
+ if conf.exists(tmp):
+ ret = True
+
+ conf.set_level(old_level)
+ return ret
+
def has_vlan_subinterface_configured(conf, intf):
"""
Checks if interface has an VLAN subinterface configured.
@@ -389,7 +412,7 @@ def get_pppoe_interfaces(conf, vrf=None):
return pppoe_interfaces
-def get_interface_dict(config, base, ifname=''):
+def get_interface_dict(config, base, ifname='', recursive_defaults=True):
"""
Common utility function to retrieve and mangle the interfaces configuration
from the CLI input nodes. All interfaces have a common base where value
@@ -405,46 +428,23 @@ def get_interface_dict(config, base, ifname=''):
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
ifname = os.environ['VYOS_TAGNODE_VALUE']
- # retrieve interface default values
- default_values = defaults(base)
-
- # We take care about VLAN (vif, vif-s, vif-c) default values later on when
- # parsing vlans in default dict and merge the "proper" values in correctly,
- # see T2665.
- for vif in ['vif', 'vif_s']:
- if vif in default_values: del default_values[vif]
-
- dict = config.get_config_dict(base + [ifname], key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
# Check if interface has been removed. We must use exists() as
# get_config_dict() will always return {} - even when an empty interface
# node like the following exists.
# +macsec macsec1 {
# +}
if not config.exists(base + [ifname]):
+ dict = config.get_config_dict(base + [ifname], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
dict.update({'deleted' : {}})
-
- # Add interface instance name into dictionary
- dict.update({'ifname': ifname})
-
- # Check if QoS policy applied on this interface - See ifconfig.interface.set_mirror_redirect()
- if config.exists(['qos', 'interface', ifname]):
- dict.update({'traffic_policy': {}})
-
- # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
- # remove the default values from the dict.
- if 'dhcpv6_options' not in dict:
- if 'dhcpv6_options' in default_values:
- del default_values['dhcpv6_options']
-
- # We have gathered the dict representation of the CLI, but there are
- # default options which we need to update into the dictionary retrived.
- # But we should only add them when interface is not deleted - as this might
- # confuse parsers
- if 'deleted' not in dict:
- dict = dict_merge(default_values, dict)
+ else:
+ # Get config_dict with default values
+ dict = config.get_config_dict(base + [ifname], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ with_defaults=True,
+ with_recursive_defaults=recursive_defaults)
# If interface does not request an IPv4 DHCP address there is no need
# to keep the dhcp-options key
@@ -452,8 +452,12 @@ def get_interface_dict(config, base, ifname=''):
if 'dhcp_options' in dict:
del dict['dhcp_options']
- # XXX: T2665: blend in proper DHCPv6-PD default values
- dict = T2665_set_dhcpv6pd_defaults(dict)
+ # Add interface instance name into dictionary
+ dict.update({'ifname': ifname})
+
+ # Check if QoS policy applied on this interface - See ifconfig.interface.set_mirror_redirect()
+ if config.exists(['qos', 'interface', ifname]):
+ dict.update({'traffic_policy': {}})
address = leaf_node_changed(config, base + [ifname, 'address'])
if address: dict.update({'address_old' : address})
@@ -474,6 +478,10 @@ def get_interface_dict(config, base, ifname=''):
dhcp = is_node_changed(config, base + [ifname, 'dhcp-options'])
if dhcp: dict.update({'dhcp_options_changed' : {}})
+ # Changine interface VRF assignemnts require a DHCP restart, too
+ dhcp = is_node_changed(config, base + [ifname, 'vrf'])
+ 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.
@@ -497,9 +505,6 @@ def get_interface_dict(config, base, ifname=''):
else:
dict['ipv6']['address'].update({'eui64_old': eui64})
- # Implant default dictionary in vif/vif-s VLAN interfaces. Values are
- # identical for all types of VLAN interfaces as they all include the same
- # XML definitions which hold the defaults.
for vif, vif_config in dict.get('vif', {}).items():
# Add subinterface name to dictionary
dict['vif'][vif].update({'ifname' : f'{ifname}.{vif}'})
@@ -507,22 +512,10 @@ def get_interface_dict(config, base, ifname=''):
if config.exists(['qos', 'interface', f'{ifname}.{vif}']):
dict['vif'][vif].update({'traffic_policy': {}})
- default_vif_values = defaults(base + ['vif'])
- # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
- # remove the default values from the dict.
- if not 'dhcpv6_options' in vif_config:
- del default_vif_values['dhcpv6_options']
-
- # Only add defaults if interface is not about to be deleted - this is
- # to keep a cleaner config dict.
if 'deleted' not in dict:
address = leaf_node_changed(config, base + [ifname, 'vif', vif, 'address'])
if address: dict['vif'][vif].update({'address_old' : address})
- dict['vif'][vif] = dict_merge(default_vif_values, dict['vif'][vif])
- # 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']:
@@ -544,26 +537,10 @@ def get_interface_dict(config, base, ifname=''):
if config.exists(['qos', 'interface', f'{ifname}.{vif_s}']):
dict['vif_s'][vif_s].update({'traffic_policy': {}})
- default_vif_s_values = defaults(base + ['vif-s'])
- # XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c
- if 'vif_c' in default_vif_s_values: del default_vif_s_values['vif_c']
-
- # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
- # remove the default values from the dict.
- if not 'dhcpv6_options' in vif_s_config:
- del default_vif_s_values['dhcpv6_options']
-
- # Only add defaults if interface is not about to be deleted - this is
- # to keep a cleaner config dict.
if 'deleted' not in dict:
address = leaf_node_changed(config, base + [ifname, 'vif-s', vif_s, 'address'])
if address: dict['vif_s'][vif_s].update({'address_old' : address})
- dict['vif_s'][vif_s] = dict_merge(default_vif_s_values,
- dict['vif_s'][vif_s])
- # 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 \
@@ -586,26 +563,11 @@ def get_interface_dict(config, base, ifname=''):
if config.exists(['qos', 'interface', f'{ifname}.{vif_s}.{vif_c}']):
dict['vif_s'][vif_s]['vif_c'][vif_c].update({'traffic_policy': {}})
- default_vif_c_values = defaults(base + ['vif-s', 'vif-c'])
-
- # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
- # remove the default values from the dict.
- if not 'dhcpv6_options' in vif_c_config:
- del default_vif_c_values['dhcpv6_options']
-
- # Only add defaults if interface is not about to be deleted - this is
- # to keep a cleaner config dict.
if 'deleted' not in dict:
address = leaf_node_changed(config, base + [ifname, 'vif-s', vif_s, 'vif-c', vif_c, 'address'])
if address: dict['vif_s'][vif_s]['vif_c'][vif_c].update(
{'address_old' : address})
- dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_merge(
- default_vif_c_values, dict['vif_s'][vif_s]['vif_c'][vif_c])
- # XXX: T2665: blend in proper DHCPv6-PD default values
- 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' \
@@ -655,45 +617,13 @@ def get_accel_dict(config, base, chap_secrets):
Return a dictionary with the necessary interface config keys.
"""
- from vyos.util import get_half_cpus
+ from vyos.utils.system import get_half_cpus
from vyos.template import is_ipv4
dict = config.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base)
-
- # T2665: defaults include RADIUS server specifics per TAG node which need to
- # be added to individual RADIUS servers instead - so we can simply delete them
- if dict_search('authentication.radius.server', default_values):
- del default_values['authentication']['radius']['server']
-
- # T2665: defaults include static-ip address per TAG node which need to be
- # added to individual local users instead - so we can simply delete them
- if dict_search('authentication.local_users.username', default_values):
- del default_values['authentication']['local_users']['username']
-
- # T2665: defaults include IPv6 client-pool mask per TAG node which need to be
- # added to individual local users instead - so we can simply delete them
- if dict_search('client_ipv6_pool.prefix.mask', default_values):
- del default_values['client_ipv6_pool']['prefix']['mask']
- # delete empty dicts
- if len (default_values['client_ipv6_pool']['prefix']) == 0:
- del default_values['client_ipv6_pool']['prefix']
- if len (default_values['client_ipv6_pool']) == 0:
- del default_values['client_ipv6_pool']
-
- # T2665: IPoE only - it has an interface tag node
- # added to individual local users instead - so we can simply delete them
- if dict_search('authentication.interface', default_values):
- del default_values['authentication']['interface']
- if dict_search('interface', default_values):
- del default_values['interface']
-
- dict = dict_merge(default_values, dict)
+ no_tag_node_value_mangle=True,
+ with_recursive_defaults=True)
# set CPUs cores to process requests
dict.update({'thread_count' : get_half_cpus()})
@@ -713,43 +643,9 @@ def get_accel_dict(config, base, chap_secrets):
dict.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6})
del dict['name_server']
- # T2665: Add individual RADIUS server default values
- if dict_search('authentication.radius.server', dict):
- default_values = defaults(base + ['authentication', 'radius', 'server'])
- for server in dict_search('authentication.radius.server', dict):
- dict['authentication']['radius']['server'][server] = dict_merge(
- default_values, dict['authentication']['radius']['server'][server])
-
- # Check option "disable-accounting" per server and replace default value from '1813' to '0'
- # set vpn sstp authentication radius server x.x.x.x disable-accounting
- if 'disable_accounting' in dict['authentication']['radius']['server'][server]:
- dict['authentication']['radius']['server'][server]['acct_port'] = '0'
-
- # T2665: Add individual local-user default values
- if dict_search('authentication.local_users.username', dict):
- default_values = defaults(base + ['authentication', 'local-users', 'username'])
- for username in dict_search('authentication.local_users.username', dict):
- dict['authentication']['local_users']['username'][username] = dict_merge(
- default_values, dict['authentication']['local_users']['username'][username])
-
- # T2665: Add individual IPv6 client-pool default mask if required
- if dict_search('client_ipv6_pool.prefix', dict):
- default_values = defaults(base + ['client-ipv6-pool', 'prefix'])
- for prefix in dict_search('client_ipv6_pool.prefix', dict):
- dict['client_ipv6_pool']['prefix'][prefix] = dict_merge(
- default_values, dict['client_ipv6_pool']['prefix'][prefix])
-
- # T2665: IPoE only - add individual local-user default values
- if dict_search('authentication.interface', dict):
- default_values = defaults(base + ['authentication', 'interface'])
- for interface in dict_search('authentication.interface', dict):
- dict['authentication']['interface'][interface] = dict_merge(
- default_values, dict['authentication']['interface'][interface])
-
- if dict_search('interface', dict):
- default_values = defaults(base + ['interface'])
- for interface in dict_search('interface', dict):
- dict['interface'][interface] = dict_merge(default_values,
- dict['interface'][interface])
+ # Check option "disable-accounting" per server and replace default value from '1813' to '0'
+ for server in (dict_search('authentication.radius.server', dict) or []):
+ if 'disable_accounting' in dict['authentication']['radius']['server'][server]:
+ dict['authentication']['radius']['server'][server]['acct_port'] = '0'
return dict
diff --git a/python/vyos/configdiff.py b/python/vyos/configdiff.py
index ac86af09c..1ec2dfafe 100644
--- a/python/vyos/configdiff.py
+++ b/python/vyos/configdiff.py
@@ -19,9 +19,10 @@ 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
-from vyos.util import dict_search_args
-from vyos.xml import defaults
+from vyos.utils.dict import get_sub_dict
+from vyos.utils.dict import mangle_dict_keys
+from vyos.utils.dict import dict_search_args
+from vyos.xml_ref import get_defaults
class ConfigDiffError(Exception):
"""
@@ -239,7 +240,9 @@ class ConfigDiff(object):
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))
+ default_values = get_defaults(self._make_path(path),
+ get_first_key=True,
+ recursive=True)
ret[k] = dict_merge(default_values, ret[k])
return ret
@@ -263,7 +266,9 @@ class ConfigDiff(object):
ret[k] = self._mangle_dict_keys(ret[k])
if k in target_defaults and not no_defaults:
- default_values = defaults(self._make_path(path))
+ default_values = get_defaults(self._make_path(path),
+ get_first_key=True,
+ recursive=True)
ret[k] = dict_merge(default_values, ret[k])
return ret
@@ -311,7 +316,9 @@ class ConfigDiff(object):
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))
+ default_values = get_defaults(self._make_path(path),
+ get_first_key=True,
+ recursive=True)
ret[k] = dict_merge(default_values, ret[k])
return ret
@@ -334,7 +341,9 @@ class ConfigDiff(object):
ret[k] = self._mangle_dict_keys(ret[k])
if k in target_defaults and not no_defaults:
- default_values = defaults(self._make_path(path))
+ default_values = get_defaults(self._make_path(path),
+ get_first_key=True,
+ recursive=True)
ret[k] = dict_merge(default_values, ret[k])
return ret
diff --git a/python/vyos/configquery.py b/python/vyos/configquery.py
index 85fef8777..71ad5b4f0 100644
--- a/python/vyos/configquery.py
+++ b/python/vyos/configquery.py
@@ -1,4 +1,4 @@
-# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2023 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -19,9 +19,11 @@ settings from op mode, and execution of arbitrary op mode commands.
'''
import os
-from subprocess import STDOUT
-from vyos.util import popen, boot_configuration_complete
+from vyos.utils.process import STDOUT
+from vyos.utils.process import popen
+
+from vyos.utils.boot import boot_configuration_complete
from vyos.config import Config
from vyos.configsource import ConfigSourceSession, ConfigSourceString
from vyos.defaults import directories
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index df44fd8d6..6d4b2af59 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -1,5 +1,5 @@
# configsession -- the write API for the VyOS running config
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation;
@@ -17,7 +17,8 @@ import re
import sys
import subprocess
-from vyos.util import is_systemd_service_running
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.dict import dict_to_paths
CLI_SHELL_API = '/bin/cli-shell-api'
SET = '/opt/vyatta/sbin/my_set'
@@ -28,7 +29,7 @@ DISCARD = '/opt/vyatta/sbin/my_discard'
SHOW_CONFIG = ['/bin/cli-shell-api', 'showConfig']
LOAD_CONFIG = ['/bin/cli-shell-api', 'loadFile']
MIGRATE_LOAD_CONFIG = ['/usr/libexec/vyos/vyos-load-config.py']
-SAVE_CONFIG = ['/opt/vyatta/sbin/vyatta-save-config.pl']
+SAVE_CONFIG = ['/usr/libexec/vyos/vyos-save-config.py']
INSTALL_IMAGE = ['/opt/vyatta/sbin/install-image', '--url']
REMOVE_IMAGE = ['/opt/vyatta/bin/vyatta-boot-image.pl', '--del']
GENERATE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'generate']
@@ -148,6 +149,13 @@ class ConfigSession(object):
value = [value]
self.__run_command([SET] + path + value)
+ def set_section(self, path: list, d: dict):
+ try:
+ for p in dict_to_paths(d):
+ self.set(path + p)
+ except (ValueError, ConfigSessionError) as e:
+ raise ConfigSessionError(e)
+
def delete(self, path, value=None):
if not value:
value = []
@@ -155,6 +163,15 @@ class ConfigSession(object):
value = [value]
self.__run_command([DELETE] + path + value)
+ def load_section(self, path: list, d: dict):
+ try:
+ self.delete(path)
+ if d:
+ for p in dict_to_paths(d):
+ self.set(path + p)
+ except (ValueError, ConfigSessionError) as e:
+ raise ConfigSessionError(e)
+
def comment(self, path, value=None):
if not value:
value = [""]
diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py
index 510b5b65a..f582bdfab 100644
--- a/python/vyos/configsource.py
+++ b/python/vyos/configsource.py
@@ -1,5 +1,5 @@
-# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2023 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -19,7 +19,7 @@ import re
import subprocess
from vyos.configtree import ConfigTree
-from vyos.util import boot_configuration_complete
+from vyos.utils.boot import boot_configuration_complete
class VyOSError(Exception):
"""
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py
index 19b9838d4..09cfd43d3 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -201,7 +201,9 @@ class ConfigTree(object):
check_path(path)
path_str = " ".join(map(str, path)).encode()
- self.__delete(self.__config, path_str)
+ res = self.__delete(self.__config, path_str)
+ if (res != 0):
+ raise ConfigTreeError(f"Path doesn't exist: {path}")
if self.__migration:
print(f"- op: delete path: {path}")
@@ -210,7 +212,14 @@ class ConfigTree(object):
check_path(path)
path_str = " ".join(map(str, path)).encode()
- self.__delete_value(self.__config, path_str, value.encode())
+ res = self.__delete_value(self.__config, path_str, value.encode())
+ if (res != 0):
+ if res == 1:
+ raise ConfigTreeError(f"Path doesn't exist: {path}")
+ elif res == 2:
+ raise ConfigTreeError(f"Value doesn't exist: '{value}'")
+ else:
+ raise ConfigTreeError()
if self.__migration:
print(f"- op: delete_value path: {path} value: {value}")
@@ -374,14 +383,16 @@ def union(left, right, libpath=LIBPATH):
return tree
def reference_tree_to_json(from_dir, to_file, libpath=LIBPATH):
- __lib = cdll.LoadLibrary(libpath)
- __reference_tree_to_json = __lib.reference_tree_to_json
- __reference_tree_to_json.argtypes = [c_char_p, c_char_p]
- __get_error = __lib.get_error
- __get_error.argtypes = []
- __get_error.restype = c_char_p
-
- res = __reference_tree_to_json(from_dir.encode(), to_file.encode())
+ try:
+ __lib = cdll.LoadLibrary(libpath)
+ __reference_tree_to_json = __lib.reference_tree_to_json
+ __reference_tree_to_json.argtypes = [c_char_p, c_char_p]
+ __get_error = __lib.get_error
+ __get_error.argtypes = []
+ __get_error.restype = c_char_p
+ res = __reference_tree_to_json(from_dir.encode(), to_file.encode())
+ except Exception as e:
+ raise ConfigTreeError(e)
if res == 1:
msg = __get_error().decode()
raise ConfigTreeError(msg)
@@ -409,10 +420,6 @@ class DiffTree:
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()
@@ -426,11 +433,7 @@ class DiffTree:
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)
+ self.delete = self.full.get_subtree(['del'])
def to_commands(self):
add = self.add.to_commands()
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 8fddd91d0..52f9238b8 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -22,8 +22,8 @@
# makes use of it!
from vyos import ConfigError
-from vyos.util import dict_search
-from vyos.util import dict_search_recursive
+from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_recursive
def verify_mtu(config):
"""
@@ -187,15 +187,14 @@ def verify_eapol(config):
if 'ca' not in config['pki']:
raise ConfigError('Invalid CA certificate specified for EAPoL')
- ca_cert_name = config['eapol']['ca_certificate']
+ for ca_cert_name in config['eapol']['ca_certificate']:
+ if ca_cert_name not in config['pki']['ca']:
+ raise ConfigError('Invalid CA certificate specified for EAPoL')
- if ca_cert_name not in config['pki']['ca']:
- raise ConfigError('Invalid CA certificate specified for EAPoL')
-
- ca_cert = config['pki']['ca'][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')
+ if 'certificate' not in ca_cert:
+ raise ConfigError('Invalid CA certificate specified for EAPoL')
def verify_mirror_redirect(config):
"""
@@ -314,15 +313,13 @@ def verify_dhcpv6(config):
recurring validation of DHCPv6 options which are mutually exclusive.
"""
if 'dhcpv6_options' in config:
- from vyos.util import dict_search
-
if {'parameters_only', 'temporary'} <= set(config['dhcpv6_options']):
raise ConfigError('DHCPv6 temporary and parameters-only options '
'are mutually exclusive!')
# It is not allowed to have duplicate SLA-IDs as those identify an
# assigned IPv6 subnet from a delegated prefix
- for pd in dict_search('dhcpv6_options.pd', config):
+ for pd in (dict_search('dhcpv6_options.pd', config) or []):
sla_ids = []
interfaces = dict_search(f'dhcpv6_options.pd.{pd}.interface', config)
@@ -460,7 +457,7 @@ def verify_diffie_hellman_length(file, min_keysize):
then or equal to min_keysize """
import os
import re
- from vyos.util import cmd
+ from vyos.utils.process import cmd
try:
keysize = str(min_keysize)
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index d4ffc249e..a5314790d 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -32,7 +32,9 @@ directories = {
'api_schema': f'{base_dir}/services/api/graphql/graphql/schema/',
'api_client_op': f'{base_dir}/services/api/graphql/graphql/client_op/',
'api_templates': f'{base_dir}/services/api/graphql/session/templates/',
- 'vyos_udev_dir' : '/run/udev/vyos'
+ 'vyos_udev_dir' : '/run/udev/vyos',
+ 'isc_dhclient_dir' : '/run/dhclient',
+ 'dhcp6_client_dir' : '/run/dhcp6c',
}
config_status = '/tmp/vyos-config-status'
diff --git a/python/vyos/dicts.py b/python/vyos/dicts.py
deleted file mode 100644
index b12cda40f..000000000
--- a/python/vyos/dicts.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-from vyos import ConfigError
-
-
-class FixedDict(dict):
- """
- FixedDict: A dictionnary not allowing new keys to be created after initialisation.
-
- >>> f = FixedDict(**{'count':1})
- >>> f['count'] = 2
- >>> f['king'] = 3
- File "...", line ..., in __setitem__
- raise ConfigError(f'Option "{k}" has no defined default')
- """
-
- def __init__(self, **options):
- self._allowed = options.keys()
- super().__init__(**options)
-
- def __setitem__(self, k, v):
- """
- __setitem__ is a builtin which is called by python when setting dict values:
- >>> d = dict()
- >>> d['key'] = 'value'
- >>> d
- {'key': 'value'}
-
- is syntaxic sugar for
-
- >>> d = dict()
- >>> d.__setitem__('key','value')
- >>> d
- {'key': 'value'}
- """
- if k not in self._allowed:
- raise ConfigError(f'Option "{k}" has no defined default')
- super().__setitem__(k, v)
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 1b1e54dfb..ca3bcfc3d 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -16,12 +16,13 @@
import os
import re
-from vyos.util import popen
+from vyos.utils.process 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', 'hv_netvsc', 'veth']
+ 'iavf', 'ice', 'i40e', 'hv_netvsc', 'veth', 'ixgbevf',
+ 'tun']
class Ethtool:
"""
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 919032a41..53ff8259e 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,11 +28,11 @@ from time import strftime
from vyos.remote import download
from vyos.template import is_ipv4
from vyos.template import render
-from vyos.util import call
-from vyos.util import cmd
-from vyos.util import dict_search_args
-from vyos.util import dict_search_recursive
-from vyos.util import run
+from vyos.utils.dict import dict_search_args
+from vyos.utils.dict import dict_search_recursive
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.process import run
# Domain Resolver
@@ -41,14 +41,19 @@ def fqdn_config_parse(firewall):
firewall['ip6_fqdn'] = {}
for domain, path in dict_search_recursive(firewall, 'fqdn'):
- fw_name = path[1] # name/ipv6-name
- rule = path[3] # rule id
- suffix = path[4][0] # source/destination (1 char)
- set_name = f'{fw_name}_{rule}_{suffix}'
+ hook_name = path[1]
+ priority = path[2]
+
+ fw_name = path[2]
+ rule = path[4]
+ suffix = path[5][0]
+ set_name = f'{hook_name}_{priority}_{rule}_{suffix}'
- if path[0] == 'name':
+ if (path[0] == 'ipv4') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):
firewall['ip_fqdn'][set_name] = domain
- elif path[0] == 'ipv6_name':
+ elif (path[0] == 'ipv6') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):
+ if path[1] == 'name':
+ set_name = f'name6_{priority}_{rule}_{suffix}'
firewall['ip6_fqdn'][set_name] = domain
def fqdn_resolve(fqdn, ipv6=False):
@@ -80,7 +85,7 @@ def nft_action(vyos_action):
return 'return'
return vyos_action
-def parse_rule(rule_conf, fw_name, rule_id, ip_name):
+def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
output = []
def_suffix = '6' if ip_name == 'ip6' else ''
@@ -129,16 +134,34 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'fqdn' in side_conf:
fqdn = side_conf['fqdn']
+ hook_name = ''
operator = ''
if fqdn[0] == '!':
operator = '!='
- output.append(f'{ip_name} {prefix}addr {operator} @FQDN_{fw_name}_{rule_id}_{prefix}')
+ if hook == 'FWD':
+ hook_name = 'forward'
+ if hook == 'INP':
+ hook_name = 'input'
+ if hook == 'OUT':
+ hook_name = 'output'
+ if hook == 'NAM':
+ hook_name = f'name{def_suffix}'
+ output.append(f'{ip_name} {prefix}addr {operator} @FQDN_{hook_name}_{fw_name}_{rule_id}_{prefix}')
if dict_search_args(side_conf, 'geoip', 'country_code'):
operator = ''
+ hook_name = ''
if dict_search_args(side_conf, 'geoip', 'inverse_match') != None:
operator = '!='
- output.append(f'{ip_name} {prefix}addr {operator} @GEOIP_CC_{fw_name}_{rule_id}')
+ if hook == 'FWD':
+ hook_name = 'forward'
+ if hook == 'INP':
+ hook_name = 'input'
+ if hook == 'OUT':
+ hook_name = 'output'
+ if hook == 'NAM':
+ hook_name = f'name'
+ output.append(f'{ip_name} {prefix}addr {operator} @GEOIP_CC{def_suffix}_{hook_name}_{fw_name}_{rule_id}')
if 'mac_address' in side_conf:
suffix = side_conf["mac_address"]
@@ -249,20 +272,34 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
output.append(f'ip6 hoplimit {operator} {value}')
if 'inbound_interface' in rule_conf:
+ operator = ''
if 'interface_name' in rule_conf['inbound_interface']:
iiface = rule_conf['inbound_interface']['interface_name']
- output.append(f'iifname {{{iiface}}}')
+ if iiface[0] == '!':
+ operator = '!='
+ iiface = iiface[1:]
+ output.append(f'iifname {operator} {{{iiface}}}')
else:
iiface = rule_conf['inbound_interface']['interface_group']
- output.append(f'iifname @I_{iiface}')
+ if iiface[0] == '!':
+ operator = '!='
+ iiface = iiface[1:]
+ output.append(f'iifname {operator} @I_{iiface}')
if 'outbound_interface' in rule_conf:
+ operator = ''
if 'interface_name' in rule_conf['outbound_interface']:
oiface = rule_conf['outbound_interface']['interface_name']
- output.append(f'oifname {{{oiface}}}')
+ if oiface[0] == '!':
+ operator = '!='
+ oiface = oiface[1:]
+ output.append(f'oifname {operator} {{{oiface}}}')
else:
oiface = rule_conf['outbound_interface']['interface_group']
- output.append(f'oifname @I_{oiface}')
+ if oiface[0] == '!':
+ operator = '!='
+ oiface = oiface[1:]
+ output.append(f'oifname {operator} @I_{oiface}')
if 'ttl' in rule_conf:
operators = {'eq': '==', 'gt': '>', 'lt': '<'}
@@ -304,7 +341,7 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'ipsec' in rule_conf:
if 'match_ipsec' in rule_conf['ipsec']:
output.append('meta ipsec == 1')
- if 'match_non_ipsec' in rule_conf['ipsec']:
+ if 'match_none' in rule_conf['ipsec']:
output.append('meta ipsec == 0')
if 'fragment' in rule_conf:
@@ -324,7 +361,7 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'recent' in rule_conf:
count = rule_conf['recent']['count']
time = rule_conf['recent']['time']
- output.append(f'add @RECENT{def_suffix}_{fw_name}_{rule_id} {{ {ip_name} saddr limit rate over {count}/{time} burst {count} packets }}')
+ output.append(f'add @RECENT{def_suffix}_{hook}_{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']))
@@ -348,7 +385,9 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
output.append(parse_policy_set(rule_conf['set'], def_suffix))
if 'action' in rule_conf:
- output.append(nft_action(rule_conf['action']))
+ # Change action=return to action=action
+ # #output.append(nft_action(rule_conf['action']))
+ output.append(f'{rule_conf["action"]}')
if 'jump' in rule_conf['action']:
target = rule_conf['jump_target']
output.append(f'NAME{def_suffix}_{target}')
@@ -365,7 +404,7 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
else:
output.append('return')
- output.append(f'comment "{fw_name}-{rule_id}"')
+ output.append(f'comment "{hook}-{fw_name}-{rule_id}"')
return " ".join(output)
def parse_tcp_flags(flags):
@@ -493,11 +532,12 @@ def geoip_update(firewall, force=False):
# Map country codes to set names
for codes, path in dict_search_recursive(firewall, 'country_code'):
- set_name = f'GEOIP_CC_{path[1]}_{path[3]}'
- if path[0] == 'name':
+ set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}'
+ if ( path[0] == 'ipv4'):
for code in codes:
ipv4_codes.setdefault(code, []).append(set_name)
- elif path[0] == 'ipv6_name':
+ elif ( path[0] == 'ipv6' ):
+ set_name = f'GEOIP_CC6_{path[1]}_{path[2]}_{path[4]}'
for code in codes:
ipv6_codes.setdefault(code, []).append(set_name)
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index a84f183ef..2e3c8a271 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -67,9 +67,12 @@ Apply the new configuration:
import tempfile
import re
-from vyos import util
-from vyos.util import chown
-from vyos.util import cmd
+
+from vyos.utils.permission import chown
+from vyos.utils.process import cmd
+from vyos.utils.process import popen
+from vyos.utils.process import STDOUT
+
import logging
from logging.handlers import SysLogHandler
import os
@@ -144,7 +147,7 @@ def get_configuration(daemon=None, marked=False):
if daemon:
cmd += f' -d {daemon}'
- output, code = util.popen(cmd, stderr=util.STDOUT)
+ output, code = popen(cmd, stderr=STDOUT)
if code:
raise OSError(code, output)
@@ -166,7 +169,7 @@ def mark_configuration(config):
config: The configuration string to mark/test
return: The marked configuration from FRR
"""
- output, code = util.popen(f"{path_vtysh} -m -f -", stderr=util.STDOUT, input=config)
+ output, code = popen(f"{path_vtysh} -m -f -", stderr=STDOUT, input=config)
if code == 2:
raise ConfigurationNotValid(str(output))
@@ -206,7 +209,7 @@ def reload_configuration(config, daemon=None):
cmd += f' {f.name}'
LOG.debug(f'reload_configuration: Executing command against frr-reload: "{cmd}"')
- output, code = util.popen(cmd, stderr=util.STDOUT)
+ output, code = popen(cmd, stderr=STDOUT)
f.close()
for i, e in enumerate(output.split('\n')):
LOG.debug(f'frr-reload output: {i:3} {e}')
@@ -235,7 +238,7 @@ def execute(command):
cmd = f"{path_vtysh} -c '{command}'"
- output, code = util.popen(cmd, stderr=util.STDOUT)
+ output, code = popen(cmd, stderr=STDOUT)
if code:
raise OSError(code, output)
@@ -267,7 +270,7 @@ def configure(lines, daemon=False):
for x in lines:
cmd += f" -c '{x}'"
- output, code = util.popen(cmd, stderr=util.STDOUT)
+ output, code = popen(cmd, stderr=STDOUT)
if code == 1:
raise ConfigurationNotValid(f'Configuration FRR failed: {repr(output)}')
elif code:
diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py
index 0edd17055..d1d7d48c4 100644
--- a/python/vyos/ifconfig/bond.py
+++ b/python/vyos/ifconfig/bond.py
@@ -16,10 +16,10 @@
import os
from vyos.ifconfig.interface import Interface
-from vyos.util import cmd
-from vyos.util import dict_search
-from vyos.validate import assert_list
-from vyos.validate import assert_positive
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
+from vyos.utils.assertion import assert_list
+from vyos.utils.assertion import assert_positive
@Interface.register
class BondIf(Interface):
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index aa818bc5f..b29e71394 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -17,10 +17,10 @@ from netifaces import interfaces
import json
from vyos.ifconfig.interface import Interface
-from vyos.validate import assert_boolean
-from vyos.validate import assert_positive
-from vyos.util import cmd
-from vyos.util import dict_search
+from vyos.utils.assertion import assert_boolean
+from vyos.utils.assertion import assert_positive
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
from vyos.configdict import get_vlan_ids
from vyos.configdict import list_diff
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index 915c1d2f9..7402da55a 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -19,10 +19,10 @@ from inspect import signature
from inspect import _empty
from vyos.ifconfig.section import Section
-from vyos.util import popen
-from vyos.util import cmd
-from vyos.util import read_file
-from vyos.util import write_file
+from vyos.utils.process import popen
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
+from vyos.utils.file import write_file
from vyos import debug
class Control(Section):
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 30bea3b86..24ce3a803 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -20,10 +20,10 @@ from glob import glob
from vyos.base import Warning
from vyos.ethtool import Ethtool
from vyos.ifconfig.interface import Interface
-from vyos.util import run
-from vyos.util import dict_search
-from vyos.util import read_file
-from vyos.validate import assert_list
+from vyos.utils.dict import dict_search
+from vyos.utils.file import read_file
+from vyos.utils.process import run
+from vyos.utils.assertion import assert_list
@Interface.register
class EthernetIf(Interface):
diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py
index 276c34cd7..fbb261a35 100644
--- a/python/vyos/ifconfig/geneve.py
+++ b/python/vyos/ifconfig/geneve.py
@@ -14,7 +14,7 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from vyos.ifconfig import Interface
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
@Interface.register
class GeneveIf(Interface):
@@ -45,6 +45,7 @@ class GeneveIf(Interface):
'parameters.ip.df' : 'df',
'parameters.ip.tos' : 'tos',
'parameters.ip.ttl' : 'ttl',
+ 'parameters.ip.innerproto' : 'innerprotoinherit',
'parameters.ipv6.flowlabel' : 'flowlabel',
}
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 85fa90653..20ea66953 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -31,24 +31,25 @@ from vyos import ConfigError
from vyos.configdict import list_diff
from vyos.configdict import dict_merge
from vyos.configdict import get_vlan_ids
+from vyos.defaults import directories
from vyos.template import render
-from vyos.util import mac2eui64
-from vyos.util import dict_search
-from vyos.util import read_file
-from vyos.util import run
-from vyos.util import get_interface_config
-from vyos.util import get_interface_namespace
-from vyos.util import is_systemd_service_active
+from vyos.utils.network import mac2eui64
+from vyos.utils.dict import dict_search
+from vyos.utils.file import read_file
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import get_interface_namespace
+from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import run
from vyos.template import is_ipv4
from vyos.template import is_ipv6
-from vyos.validate import is_intf_addr_assigned
-from vyos.validate import is_ipv6_link_local
-from vyos.validate import assert_boolean
-from vyos.validate import assert_list
-from vyos.validate import assert_mac
-from vyos.validate import assert_mtu
-from vyos.validate import assert_positive
-from vyos.validate import assert_range
+from vyos.utils.network import is_intf_addr_assigned
+from vyos.utils.network import is_ipv6_link_local
+from vyos.utils.assertion import assert_boolean
+from vyos.utils.assertion import assert_list
+from vyos.utils.assertion import assert_mac
+from vyos.utils.assertion import assert_mtu
+from vyos.utils.assertion import assert_positive
+from vyos.utils.assertion import assert_range
from vyos.ifconfig.control import Control
from vyos.ifconfig.vrrp import VRRP
@@ -197,6 +198,10 @@ class Interface(Control):
'validate': lambda fwd: assert_range(fwd,0,2),
'location': '/proc/sys/net/ipv6/conf/{ifname}/forwarding',
},
+ 'ipv6_accept_dad': {
+ 'validate': lambda dad: assert_range(dad,0,3),
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_dad',
+ },
'ipv6_dad_transmits': {
'validate': assert_positive,
'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
@@ -226,6 +231,10 @@ class Interface(Control):
'validate': lambda link: assert_range(link,0,3),
'location': '/proc/sys/net/ipv4/conf/{ifname}/link_filter',
},
+ 'per_client_thread': {
+ 'validate': assert_boolean,
+ 'location': '/sys/class/net/{ifname}/threaded',
+ },
}
_sysfs_get = {
@@ -262,6 +271,9 @@ class Interface(Control):
'ipv6_forwarding': {
'location': '/proc/sys/net/ipv6/conf/{ifname}/forwarding',
},
+ 'ipv6_accept_dad': {
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_dad',
+ },
'ipv6_dad_transmits': {
'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
},
@@ -274,6 +286,10 @@ class Interface(Control):
'link_detect': {
'location': '/proc/sys/net/ipv4/conf/{ifname}/link_filter',
},
+ 'per_client_thread': {
+ 'validate': assert_boolean,
+ 'location': '/sys/class/net/{ifname}/threaded',
+ },
}
@classmethod
@@ -793,6 +809,30 @@ class Interface(Control):
return None
return self.set_interface('rp_filter', value)
+ def _cleanup_ipv6_source_validation_rules(self, ifname):
+ commands = []
+ results = self._cmd(f'nft -a list chain ip6 raw vyos_rpfilter').split("\n")
+ for line in results:
+ if f'iifname "{ifname}"' in line:
+ handle_search = re.search('handle (\d+)', line)
+ if handle_search:
+ self._cmd(f'nft delete rule ip6 raw vyos_rpfilter handle {handle_search[1]}')
+
+ def set_ipv6_source_validation(self, mode):
+ """
+ Set IPv6 reverse path validation
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_ipv6_source_validation('strict')
+ """
+ self._cleanup_ipv6_source_validation_rules(self.ifname)
+ nft_prefix = f'nft add rule ip6 raw vyos_rpfilter iifname "{self.ifname}"'
+ if mode == 'strict':
+ self._cmd(f"{nft_prefix} fib saddr . iif oif 0 counter drop")
+ elif mode == 'loose':
+ self._cmd(f"{nft_prefix} fib saddr oif 0 counter drop")
+
def set_ipv6_accept_ra(self, accept_ra):
"""
Accept Router Advertisements; autoconfigure using them.
@@ -877,6 +917,13 @@ class Interface(Control):
return None
return self.set_interface('ipv6_forwarding', forwarding)
+ def set_ipv6_dad_accept(self, dad):
+ """Whether to accept DAD (Duplicate Address Detection)"""
+ tmp = self.get_interface('ipv6_accept_dad')
+ if tmp == dad:
+ return None
+ return self.set_interface('ipv6_accept_dad', dad)
+
def set_ipv6_dad_messages(self, dad):
"""
The amount of Duplicate Address Detection probes to send.
@@ -1278,44 +1325,49 @@ class Interface(Control):
raise ValueError()
ifname = self.ifname
- config_base = r'/var/lib/dhcp/dhclient'
- config_file = f'{config_base}_{ifname}.conf'
- options_file = f'{config_base}_{ifname}.options'
- pid_file = f'{config_base}_{ifname}.pid'
- lease_file = f'{config_base}_{ifname}.leases'
+ config_base = directories['isc_dhclient_dir'] + '/dhclient'
+ dhclient_config_file = f'{config_base}_{ifname}.conf'
+ dhclient_lease_file = f'{config_base}_{ifname}.leases'
+ systemd_override_file = f'/run/systemd/system/dhclient@{ifname}.service.d/10-override.conf'
systemd_service = f'dhclient@{ifname}.service'
+ # Rendered client configuration files require the apsolute config path
+ self.config['isc_dhclient_dir'] = directories['isc_dhclient_dir']
+
# '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:
+ if enable and 'disable' not in self.config:
+ if dict_search('dhcp_options.host_name', self.config) == None:
# read configured system hostname.
# maybe change to vyos hostd client ???
hostname = 'vyos'
with open('/etc/hostname', 'r') as f:
hostname = f.read().rstrip('\n')
tmp = {'dhcp_options' : { 'host_name' : hostname}}
- self._config = dict_merge(tmp, self._config)
+ self.config = dict_merge(tmp, self.config)
- render(options_file, 'dhcp-client/daemon-options.j2', self._config)
- render(config_file, 'dhcp-client/ipv4.j2', self._config)
+ render(systemd_override_file, 'dhcp-client/override.conf.j2', self.config)
+ render(dhclient_config_file, 'dhcp-client/ipv4.j2', self.config)
+
+ # Reload systemd unit definitons as some options are dynamically generated
+ self._cmd('systemctl daemon-reload')
# 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):
+ 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]:
+ for file in [dhclient_config_file, systemd_override_file, dhclient_lease_file]:
if os.path.isfile(file):
os.remove(file)
+ return None
def set_dhcpv6(self, enable):
"""
@@ -1325,11 +1377,20 @@ class Interface(Control):
raise ValueError()
ifname = self.ifname
- config_file = f'/run/dhcp6c/dhcp6c.{ifname}.conf'
+ config_base = directories['dhcp6_client_dir']
+ config_file = f'{config_base}/dhcp6c.{ifname}.conf'
+ systemd_override_file = f'/run/systemd/system/dhcp6c@{ifname}.service.d/10-override.conf'
systemd_service = f'dhcp6c@{ifname}.service'
- if enable and 'disable' not in self._config:
- render(config_file, 'dhcp-client/ipv6.j2', self._config)
+ # Rendered client configuration files require the apsolute config path
+ self.config['dhcp6_client_dir'] = directories['dhcp6_client_dir']
+
+ if enable and 'disable' not in self.config:
+ render(systemd_override_file, 'dhcp-client/ipv6.override.conf.j2', self.config)
+ render(config_file, 'dhcp-client/ipv6.j2', self.config)
+
+ # Reload systemd unit definitons as some options are dynamically generated
+ self._cmd('systemctl daemon-reload')
# We must ignore any return codes. This is required to enable
# DHCPv6-PD for interfaces which are yet not up and running.
@@ -1340,6 +1401,8 @@ class Interface(Control):
if os.path.isfile(config_file):
os.remove(config_file)
+ return None
+
def set_mirror_redirect(self):
# Please refer to the document for details
# - https://man7.org/linux/man-pages/man8/tc.8.html
@@ -1351,20 +1414,20 @@ class Interface(Control):
if 'netns' in self.config:
return None
- source_if = self._config['ifname']
+ source_if = self.config['ifname']
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']))
- mirror_config = self._config['is_mirror_intf'][source_if].get('mirror', 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']))
+ 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
- if not 'traffic_policy' in self._config:
+ if not 'traffic_policy' in self.config:
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');
@@ -1388,43 +1451,39 @@ class Interface(Control):
if err: print('tc qdisc(filter for mirror port failed')
# Apply interface traffic redirection policy
- elif 'redirect' in self._config:
+ 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']
+ 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):
+ def set_per_client_thread(self, enable):
"""
- Enable Kernel XDP support. State can be either True or False.
+ Per-device control to enable/disable the threaded mode for all the napi
+ instances of the given network device, without the need for a device up/down.
+
+ User sets it to 1 or 0 to enable or disable threaded mode.
Example:
>>> from vyos.ifconfig import Interface
- >>> i = Interface('eth0')
- >>> i.set_xdp(True)
- """
- if not isinstance(state, bool):
- raise ValueError("Value out of range")
-
- # https://vyos.dev/T3448 - there is (yet) no RPI support for XDP
- if not os.path.exists('/usr/sbin/xdp_loader'):
- return
-
- ifname = self.config['ifname']
- cmd = f'xdp_loader -d {ifname} -U --auto-mode'
- if state:
- # Using 'xdp' will automatically decide if the driver supports
- # 'xdpdrv' or only 'xdpgeneric'. A user later sees which driver is
- # actually in use by calling 'ip a' or 'show interfaces ethernet'
- cmd = f'xdp_loader -d {ifname} --auto-mode -F --progsec xdp_router ' \
- f'--filename /usr/share/vyos/xdp/xdp_prog_kern.o && ' \
- f'xdp_prog_user -d {ifname}'
+ >>> Interface('wg1').set_per_client_thread(1)
+ """
+ # In the case of a "virtual" interface like wireguard, the sysfs
+ # node is only created once there is a peer configured. We can now
+ # add a verify() code-path for this or make this dynamic without
+ # nagging the user
+ tmp = self._sysfs_get['per_client_thread']['location']
+ if not os.path.exists(tmp):
+ return None
- return self._cmd(cmd)
+ tmp = self.get_interface('per_client_thread')
+ if tmp == enable:
+ return None
+ self.set_interface('per_client_thread', enable)
def update(self, config):
""" General helper function which works on a dictionary retrived by
@@ -1439,7 +1498,7 @@ class Interface(Control):
# Cache the configuration - it will be reused inside e.g. DHCP handler
# XXX: maybe pass the option via __init__ in the future and rename this
# method to apply()?
- self._config = config
+ self.config = config
# Change interface MAC address - re-set to real hardware address (hw-id)
# if custom mac is removed. Skip if bond member.
@@ -1576,6 +1635,11 @@ class Interface(Control):
value = tmp if (tmp != None) else '0'
self.set_ipv4_source_validation(value)
+ # IPv6 source-validation
+ tmp = dict_search('ipv6.source_validation', config)
+ value = tmp if (tmp != None) else '0'
+ self.set_ipv6_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
@@ -1605,10 +1669,17 @@ class Interface(Control):
value = '1' if (tmp != None) else '0'
self.set_ipv6_autoconf(value)
- # IPv6 Duplicate Address Detection (DAD) tries
+ # Whether to accept IPv6 DAD (Duplicate Address Detection) packets
+ tmp = dict_search('ipv6.accept_dad', config)
+ # Not all interface types got this CLI option, but if they do, there
+ # is an XML defaultValue available
+ if (tmp != None): self.set_ipv6_dad_accept(tmp)
+
+ # IPv6 DAD tries
tmp = dict_search('ipv6.dup_addr_detect_transmits', config)
- value = tmp if (tmp != None) else '1'
- self.set_ipv6_dad_messages(value)
+ # Not all interface types got this CLI option, but if they do, there
+ # is an XML defaultValue available
+ if (tmp != None): self.set_ipv6_dad_messages(tmp)
# Delete old IPv6 EUI64 addresses before changing MAC
for addr in (dict_search('ipv6.address.eui64_old', config) or []):
@@ -1631,12 +1702,14 @@ class Interface(Control):
tmp = config.get('is_bridge_member')
self.add_to_bridge(tmp)
- # eXpress Data Path - highly experimental
- self.set_xdp('xdp' in config)
-
# configure interface mirror or redirection target
self.set_mirror_redirect()
+ # enable/disable NAPI threading mode
+ tmp = dict_search('per_client_thread', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_per_client_thread(value)
+
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py
index fcd1fbf81..85a89ef8b 100644
--- a/python/vyos/ifconfig/l2tpv3.py
+++ b/python/vyos/ifconfig/l2tpv3.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -15,7 +15,8 @@
from time import sleep
from time import time
-from vyos.util import run
+
+from vyos.utils.process import run
from vyos.ifconfig.interface import Interface
def wait_for_add_l2tpv3(timeout=10, sleep_interval=1, cmd=None):
diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py
index 1a78d18d8..9329c5ee7 100644
--- a/python/vyos/ifconfig/macsec.py
+++ b/python/vyos/ifconfig/macsec.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2023 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
@@ -41,10 +41,30 @@ class MACsecIf(Interface):
Create MACsec interface in OS kernel. Interface is administrative
down by default.
"""
+
# create tunnel interface
cmd = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config)
cmd += f' cipher {self.config["security"]["cipher"]}'
self._cmd(cmd)
+ # Check if using static keys
+ if 'static' in self.config["security"]:
+ # Set static TX key
+ cmd = 'ip macsec add {ifname} tx sa 0 pn 1 on key 00'.format(**self.config)
+ cmd += f' {self.config["security"]["static"]["key"]}'
+ self._cmd(cmd)
+
+ for peer, peer_config in self.config["security"]["static"]["peer"].items():
+ if 'disable' in peer_config:
+ continue
+
+ # Create the address
+ cmd = 'ip macsec add {ifname} rx port 1 address'.format(**self.config)
+ cmd += f' {peer_config["mac"]}'
+ self._cmd(cmd)
+ # Add the rx-key to the address
+ cmd += f' sa 0 pn 1 on key 01 {peer_config["key"]}'
+ self._cmd(cmd)
+
# interface is always A/D down. It needs to be enabled explicitly
self.set_admin_state('down')
diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py
index 437fe0cae..febf1452d 100644
--- a/python/vyos/ifconfig/pppoe.py
+++ b/python/vyos/ifconfig/pppoe.py
@@ -14,8 +14,8 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from vyos.ifconfig.interface import Interface
-from vyos.validate import assert_range
-from vyos.util import get_interface_config
+from vyos.utils.assertion import assert_range
+from vyos.utils.network import get_interface_config
@Interface.register
class PPPoEIf(Interface):
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index b7bf7d982..9ba7b31a6 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -17,8 +17,8 @@
# https://community.hetzner.com/tutorials/linux-setup-gre-tunnel
from vyos.ifconfig.interface import Interface
-from vyos.util import dict_search
-from vyos.validate import assert_list
+from vyos.utils.dict import dict_search
+from vyos.utils.assertion import assert_list
def enable_to_on(value):
if value == 'enable':
diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py
index 47aaadecd..fde903a53 100644
--- a/python/vyos/ifconfig/vrrp.py
+++ b/python/vyos/ifconfig/vrrp.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -21,8 +21,11 @@ from time import time
from time import sleep
from tabulate import tabulate
-from vyos import util
from vyos.configquery import ConfigTreeQuery
+from vyos.utils.convert import seconds_to_human
+from vyos.utils.file import read_file
+from vyos.utils.file import wait_for_file_write_complete
+from vyos.utils.process import process_running
class VRRPError(Exception):
pass
@@ -84,21 +87,21 @@ class VRRP(object):
def is_running(cls):
if not os.path.exists(cls.location['pid']):
return False
- return util.process_running(cls.location['pid'])
+ return process_running(cls.location['pid'])
@classmethod
def collect(cls, what):
fname = cls.location[what]
try:
# send signal to generate the configuration file
- pid = util.read_file(cls.location['pid'])
- util.wait_for_file_write_complete(fname,
+ pid = read_file(cls.location['pid'])
+ wait_for_file_write_complete(fname,
pre_hook=(lambda: os.kill(int(pid), cls._signal[what])),
timeout=30)
- return util.read_file(fname)
+ return read_file(fname)
except OSError:
- # raised by vyos.util.read_file
+ # raised by vyos.utils.file.read_file
raise VRRPNoData("VRRP data is not available (wait time exceeded)")
except FileNotFoundError:
raise VRRPNoData("VRRP data is not available (process not running or no active groups)")
@@ -145,7 +148,7 @@ class VRRP(object):
priority = data['effective_priority']
since = int(time() - float(data['last_transition']))
- last = util.seconds_to_human(since)
+ last = seconds_to_human(since)
groups.append([name, intf, vrid, state, priority, last])
diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py
index dc99d365a..9ebbeb9ed 100644
--- a/python/vyos/ifconfig/vti.py
+++ b/python/vyos/ifconfig/vti.py
@@ -14,7 +14,7 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from vyos.ifconfig.interface import Interface
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
@Interface.register
class VTIIf(Interface):
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 5baff10a9..6a9911588 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -15,7 +15,7 @@
from vyos import ConfigError
from vyos.ifconfig import Interface
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
@Interface.register
class VXLANIf(Interface):
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index fe5e9c519..4aac103ec 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -25,6 +25,7 @@ from hurry.filesize import alternative
from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
from vyos.template import is_ipv6
+from vyos.base import Warning
class WireGuardOperational(Operational):
def _dump(self):
@@ -184,7 +185,6 @@ class WireGuardIf(Interface):
base_cmd += f' private-key {tmp_file.name}'
base_cmd = base_cmd.format(**config)
-
if 'peer' in config:
for peer, peer_config in config['peer'].items():
# T4702: No need to configure this peer when it was explicitly
diff --git a/python/vyos/initialsetup.py b/python/vyos/initialsetup.py
index 574e7892d..3b280dc6b 100644
--- a/python/vyos/initialsetup.py
+++ b/python/vyos/initialsetup.py
@@ -1,7 +1,7 @@
# initialsetup -- functions for setting common values in config file,
# for use in installation and first boot scripts
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation;
@@ -12,10 +12,12 @@
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import vyos.configtree
-import vyos.authutils
+
+from vyos.utils.auth import make_password_hash
+from vyos.utils.auth import split_ssh_public_key
def set_interface_address(config, intf, addr, intf_type="ethernet"):
config.set(["interfaces", intf_type, intf, "address"], value=addr)
@@ -35,8 +37,8 @@ def set_default_gateway(config, gateway):
def set_user_password(config, user, password):
# Make a password hash
- hash = vyos.authutils.make_password_hash(password)
-
+ hash = make_password_hash(password)
+
config.set(["system", "login", "user", user, "authentication", "encrypted-password"], value=hash)
config.set(["system", "login", "user", user, "authentication", "plaintext-password"], value="")
@@ -48,7 +50,7 @@ def set_user_level(config, user, level):
config.set(["system", "login", "user", user, "level"], value=level)
def set_user_ssh_key(config, user, key_string):
- key = vyos.authutils.split_ssh_public_key(key_string, defaultname=user)
+ key = split_ssh_public_key(key_string, defaultname=user)
config.set(["system", "login", "user", user, "authentication", "public-keys", key["name"], "key"], value=key["data"])
config.set(["system", "login", "user", user, "authentication", "public-keys", key["name"], "type"], value=key["type"])
diff --git a/python/vyos/ipsec.py b/python/vyos/ipsec.py
index bb5611025..4603aab22 100644
--- a/python/vyos/ipsec.py
+++ b/python/vyos/ipsec.py
@@ -33,9 +33,11 @@ def get_vici_sas():
session = vici_session()
except Exception:
raise ViciInitiateError("IPsec not initialized")
- sas = list(session.list_sas())
- return sas
-
+ try:
+ sas = list(session.list_sas())
+ return sas
+ except Exception:
+ raise ViciCommandError(f'Failed to get SAs')
def get_vici_connections():
from vici import Session as vici_session
@@ -44,9 +46,11 @@ def get_vici_connections():
session = vici_session()
except Exception:
raise ViciInitiateError("IPsec not initialized")
- connections = list(session.list_conns())
- return connections
-
+ try:
+ connections = list(session.list_conns())
+ return connections
+ except Exception:
+ raise ViciCommandError(f'Failed to get connections')
def get_vici_sas_by_name(ike_name: str, tunnel: str) -> list:
"""
diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py
index 87c74e1ea..872682bc0 100644
--- a/python/vyos/migrator.py
+++ b/python/vyos/migrator.py
@@ -20,7 +20,7 @@ import logging
import vyos.defaults
import vyos.component_version as component_version
-from vyos.util import cmd
+from vyos.utils.process import cmd
log_file = os.path.join(vyos.defaults.directories['config'], 'vyos-migrate.log')
diff --git a/python/vyos/nat.py b/python/vyos/nat.py
index 53fd7fb33..9cbc2b96e 100644
--- a/python/vyos/nat.py
+++ b/python/vyos/nat.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from vyos.template import is_ip_network
-from vyos.util import dict_search_args
+from vyos.utils.dict import dict_search_args
from vyos.template import bracketize_ipv6
@@ -54,28 +54,35 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
translation_str = 'return'
log_suffix = '-EXCL'
elif 'translation' in rule_conf:
- translation_prefix = nat_type[:1]
- translation_output = [f'{translation_prefix}nat']
addr = dict_search_args(rule_conf, 'translation', 'address')
port = dict_search_args(rule_conf, 'translation', 'port')
+ if 'redirect' in rule_conf['translation']:
+ translation_output = [f'redirect']
+ redirect_port = dict_search_args(rule_conf, 'translation', 'redirect', 'port')
+ if redirect_port:
+ translation_output.append(f'to {redirect_port}')
+ else:
- if addr and is_ip_network(addr):
- if not ipv6:
- map_addr = dict_search_args(rule_conf, nat_type, 'address')
- translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}')
- ignore_type_addr = True
+ translation_prefix = nat_type[:1]
+ translation_output = [f'{translation_prefix}nat']
+
+ if addr and is_ip_network(addr):
+ if not ipv6:
+ map_addr = dict_search_args(rule_conf, nat_type, 'address')
+ translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}')
+ ignore_type_addr = True
+ else:
+ translation_output.append(f'prefix to {addr}')
+ elif addr == 'masquerade':
+ if port:
+ addr = f'{addr} to '
+ translation_output = [addr]
+ log_suffix = '-MASQ'
else:
- translation_output.append(f'prefix to {addr}')
- elif addr == 'masquerade':
- if port:
- addr = f'{addr} to '
- translation_output = [addr]
- log_suffix = '-MASQ'
- else:
- translation_output.append('to')
- if addr:
- addr = bracketize_ipv6(addr)
- translation_output.append(addr)
+ translation_output.append('to')
+ if addr:
+ addr = bracketize_ipv6(addr)
+ translation_output.append(addr)
options = []
addr_mapping = dict_search_args(rule_conf, 'translation', 'options', 'address_mapping')
@@ -90,6 +97,39 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
if options:
translation_str += f' {",".join(options)}'
+ if not ipv6 and 'backend' in rule_conf['load_balance']:
+ hash_input_items = []
+ current_prob = 0
+ nat_map = []
+
+ for trans_addr, addr in rule_conf['load_balance']['backend'].items():
+ item_prob = int(addr['weight'])
+ upper_limit = current_prob + item_prob - 1
+ hash_val = str(current_prob) + '-' + str(upper_limit)
+ element = hash_val + " : " + trans_addr
+ nat_map.append(element)
+ current_prob = current_prob + item_prob
+
+ elements = ' , '.join(nat_map)
+
+ if 'hash' in rule_conf['load_balance'] and 'random' in rule_conf['load_balance']['hash']:
+ translation_str += ' numgen random mod 100 map ' + '{ ' + f'{elements}' + ' }'
+ else:
+ for input_param in rule_conf['load_balance']['hash']:
+ if input_param == 'source-address':
+ param = 'ip saddr'
+ elif input_param == 'destination-address':
+ param = 'ip daddr'
+ elif input_param == 'source-port':
+ prot = rule_conf['protocol']
+ param = f'{prot} sport'
+ elif input_param == 'destination-port':
+ prot = rule_conf['protocol']
+ param = f'{prot} dport'
+ hash_input_items.append(param)
+ hash_input = ' . '.join(hash_input_items)
+ translation_str += f' jhash ' + f'{hash_input}' + ' mod 100 map ' + '{ ' + f'{elements}' + ' }'
+
for target in ['source', 'destination']:
if target not in rule_conf:
continue
diff --git a/python/vyos/pki.py b/python/vyos/pki.py
index cd15e3878..792e24b76 100644
--- a/python/vyos/pki.py
+++ b/python/vyos/pki.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2023 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
@@ -63,6 +63,18 @@ private_format_map = {
'OpenSSH': serialization.PrivateFormat.OpenSSH
}
+hash_map = {
+ 'sha256': hashes.SHA256,
+ 'sha384': hashes.SHA384,
+ 'sha512': hashes.SHA512,
+}
+
+def get_certificate_fingerprint(cert, hash):
+ hash_algorithm = hash_map[hash]()
+ fp = cert.fingerprint(hash_algorithm)
+
+ return fp.hex(':').upper()
+
def encode_certificate(cert):
return cert.public_bytes(encoding=serialization.Encoding.PEM).decode('utf-8')
diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py
index 33bb8ae28..d8bbfe970 100644
--- a/python/vyos/qos/base.py
+++ b/python/vyos/qos/base.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2022-2023 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
@@ -16,14 +16,52 @@
import os
from vyos.base import Warning
-from vyos.util import cmd
-from vyos.util import dict_search
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
+from vyos.utils.file import read_file
+
+from vyos.utils.network import get_protocol_by_name
+
class QoSBase:
_debug = False
_direction = ['egress']
_parent = 0xffff
+ _dsfields = {
+ "default": 0x0,
+ "lowdelay": 0x10,
+ "throughput": 0x08,
+ "reliability": 0x04,
+ "mincost": 0x02,
+ "priority": 0x20,
+ "immediate": 0x40,
+ "flash": 0x60,
+ "flash-override": 0x80,
+ "critical": 0x0A,
+ "internet": 0xC0,
+ "network": 0xE0,
+ "AF11": 0x28,
+ "AF12": 0x30,
+ "AF13": 0x38,
+ "AF21": 0x48,
+ "AF22": 0x50,
+ "AF23": 0x58,
+ "AF31": 0x68,
+ "AF32": 0x70,
+ "AF33": 0x78,
+ "AF41": 0x88,
+ "AF42": 0x90,
+ "AF43": 0x98,
+ "CS1": 0x20,
+ "CS2": 0x40,
+ "CS3": 0x60,
+ "CS4": 0x80,
+ "CS5": 0xA0,
+ "CS6": 0xC0,
+ "CS7": 0xE0,
+ "EF": 0xB8
+ }
+ qostype = None
def __init__(self, interface):
if os.path.exists('/tmp/vyos.qos.debug'):
@@ -45,6 +83,12 @@ class QoSBase:
return tmp[-1]
return None
+ def _get_dsfield(self, value):
+ if value in self._dsfields:
+ return self._dsfields[value]
+ else:
+ return value
+
def _build_base_qdisc(self, config : dict, cls_id : int):
"""
Add/replace qdisc for every class (also default is a class). This is
@@ -63,7 +107,8 @@ class QoSBase:
queue_limit = dict_search('queue_limit', config)
for ii in range(1, 4):
- tmp = f'tc qdisc replace dev {self._interface} parent {handle:x}:{ii:x} pfifo limit {queue_limit}'
+ tmp = f'tc qdisc replace dev {self._interface} parent {handle:x}:{ii:x} pfifo'
+ if queue_limit: tmp += f' limit {queue_limit}'
self._cmd(tmp)
elif queue_type == 'fair-queue':
@@ -160,18 +205,21 @@ class QoSBase:
self._build_base_qdisc(cls_config, int(cls))
# every match criteria has it's tc instance
- filter_cmd = f'tc filter replace dev {self._interface} parent {self._parent:x}:'
+ filter_cmd_base = f'tc filter add dev {self._interface} parent {self._parent:x}:'
if priority:
- filter_cmd += f' prio {cls}'
+ filter_cmd_base += f' prio {cls}'
elif 'priority' in cls_config:
prio = cls_config['priority']
- filter_cmd += f' prio {prio}'
+ filter_cmd_base += f' prio {prio}'
- filter_cmd += ' protocol all'
+ filter_cmd_base += ' protocol all'
if 'match' in cls_config:
- for match, match_config in cls_config['match'].items():
+ for index, (match, match_config) in enumerate(cls_config['match'].items(), start=1):
+ filter_cmd = filter_cmd_base
+ if self.qostype == 'shaper' and 'prio ' not in filter_cmd:
+ filter_cmd += f' prio {index}'
if 'mark' in match_config:
mark = match_config['mark']
filter_cmd += f' handle {mark} fw'
@@ -197,7 +245,17 @@ class QoSBase:
if tmp: filter_cmd += f' match {tc_af} dport {tmp} 0xffff'
tmp = dict_search(f'{af}.protocol', match_config)
- if tmp: filter_cmd += f' match {tc_af} protocol {tmp} 0xff'
+ if tmp:
+ tmp = get_protocol_by_name(tmp)
+ filter_cmd += f' match {tc_af} protocol {tmp} 0xff'
+
+ tmp = dict_search(f'{af}.dscp', match_config)
+ if tmp:
+ tmp = self._get_dsfield(tmp)
+ if af == 'ip':
+ filter_cmd += f' match {tc_af} dsfield {tmp} 0xff'
+ elif af == 'ipv6':
+ filter_cmd += f' match u16 {tmp} 0x0ff0 at 0'
# Will match against total length of an IPv4 packet and
# payload length of an IPv6 packet.
@@ -236,67 +294,100 @@ class QoSBase:
elif af == 'ipv6':
filter_cmd += f' match u8 {mask} {mask} at 53'
+ cls = int(cls)
+ filter_cmd += f' flowid {self._parent:x}:{cls:x}'
+ self._cmd(filter_cmd)
+
+ if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config):
+ filter_cmd += f' action police'
+
+ if 'exceed' in cls_config:
+ action = cls_config['exceed']
+ filter_cmd += f' conform-exceed {action}'
+ if 'not_exceed' in cls_config:
+ action = cls_config['not_exceed']
+ filter_cmd += f'/{action}'
+
+ if 'bandwidth' in cls_config:
+ rate = self._rate_convert(cls_config['bandwidth'])
+ filter_cmd += f' rate {rate}'
+
+ if 'burst' in cls_config:
+ burst = cls_config['burst']
+ filter_cmd += f' burst {burst}'
+ cls = int(cls)
+ filter_cmd += f' flowid {self._parent:x}:{cls:x}'
+ self._cmd(filter_cmd)
+
else:
filter_cmd += ' basic'
+ cls = int(cls)
+ filter_cmd += f' flowid {self._parent:x}:{cls:x}'
+ self._cmd(filter_cmd)
+
+
# The police block allows limiting of the byte or packet rate of
# traffic matched by the filter it is attached to.
# https://man7.org/linux/man-pages/man8/tc-police.8.html
- if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config):
- filter_cmd += f' action police'
-
- if 'exceed' in cls_config:
- action = cls_config['exceed']
- filter_cmd += f' conform-exceed {action}'
- if 'not_exceed' in cls_config:
- action = cls_config['not_exceed']
- filter_cmd += f'/{action}'
- if 'bandwidth' in cls_config:
- rate = self._rate_convert(cls_config['bandwidth'])
- filter_cmd += f' rate {rate}'
-
- if 'burst' in cls_config:
- burst = cls_config['burst']
- filter_cmd += f' burst {burst}'
-
- cls = int(cls)
- filter_cmd += f' flowid {self._parent:x}:{cls:x}'
- self._cmd(filter_cmd)
+ # T5295: We do not handle rate via tc filter directly,
+ # but rather set the tc filter to direct traffic to the correct tc class flow.
+ #
+ # if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config):
+ # filter_cmd += f' action police'
+ #
+ # if 'exceed' in cls_config:
+ # action = cls_config['exceed']
+ # filter_cmd += f' conform-exceed {action}'
+ # if 'not_exceed' in cls_config:
+ # action = cls_config['not_exceed']
+ # filter_cmd += f'/{action}'
+ #
+ # if 'bandwidth' in cls_config:
+ # rate = self._rate_convert(cls_config['bandwidth'])
+ # filter_cmd += f' rate {rate}'
+ #
+ # if 'burst' in cls_config:
+ # burst = cls_config['burst']
+ # filter_cmd += f' burst {burst}'
if 'default' in config:
+ default_cls_id = 1
if 'class' in config:
class_id_max = self._get_class_max_id(config)
default_cls_id = int(class_id_max) +1
- self._build_base_qdisc(config['default'], default_cls_id)
+ self._build_base_qdisc(config['default'], default_cls_id)
- filter_cmd = f'tc filter replace dev {self._interface} parent {self._parent:x}: '
- filter_cmd += 'prio 255 protocol all basic'
+ if self.qostype == 'limiter':
+ if 'default' in config:
+ filter_cmd = f'tc filter replace dev {self._interface} parent {self._parent:x}: '
+ filter_cmd += 'prio 255 protocol all basic'
- # The police block allows limiting of the byte or packet rate of
- # traffic matched by the filter it is attached to.
- # https://man7.org/linux/man-pages/man8/tc-police.8.html
- if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in config['default']):
- filter_cmd += f' action police'
-
- if 'exceed' in config['default']:
- action = config['default']['exceed']
- filter_cmd += f' conform-exceed {action}'
- if 'not_exceed' in config['default']:
- action = config['default']['not_exceed']
- filter_cmd += f'/{action}'
+ # The police block allows limiting of the byte or packet rate of
+ # traffic matched by the filter it is attached to.
+ # https://man7.org/linux/man-pages/man8/tc-police.8.html
+ if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in
+ config['default']):
+ filter_cmd += f' action police'
- if 'bandwidth' in config['default']:
- rate = self._rate_convert(config['default']['bandwidth'])
- filter_cmd += f' rate {rate}'
+ if 'exceed' in config['default']:
+ action = config['default']['exceed']
+ filter_cmd += f' conform-exceed {action}'
+ if 'not_exceed' in config['default']:
+ action = config['default']['not_exceed']
+ filter_cmd += f'/{action}'
- if 'burst' in config['default']:
- burst = config['default']['burst']
- filter_cmd += f' burst {burst}'
+ if 'bandwidth' in config['default']:
+ rate = self._rate_convert(config['default']['bandwidth'])
+ filter_cmd += f' rate {rate}'
- if 'class' in config:
- filter_cmd += f' flowid {self._parent:x}:{default_cls_id:x}'
+ if 'burst' in config['default']:
+ burst = config['default']['burst']
+ filter_cmd += f' burst {burst}'
- self._cmd(filter_cmd)
+ if 'class' in config:
+ filter_cmd += f' flowid {self._parent:x}:{default_cls_id:x}'
+ self._cmd(filter_cmd)
diff --git a/python/vyos/qos/limiter.py b/python/vyos/qos/limiter.py
index ace0c0b6c..3f5c11112 100644
--- a/python/vyos/qos/limiter.py
+++ b/python/vyos/qos/limiter.py
@@ -17,6 +17,7 @@ from vyos.qos.base import QoSBase
class Limiter(QoSBase):
_direction = ['ingress']
+ qostype = 'limiter'
def update(self, config, direction):
tmp = f'tc qdisc add dev {self._interface} handle {self._parent:x}: {direction}'
diff --git a/python/vyos/qos/priority.py b/python/vyos/qos/priority.py
index 6d4a60a43..8182400f9 100644
--- a/python/vyos/qos/priority.py
+++ b/python/vyos/qos/priority.py
@@ -14,7 +14,7 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from vyos.qos.base import QoSBase
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
class Priority(QoSBase):
_parent = 1
diff --git a/python/vyos/qos/trafficshaper.py b/python/vyos/qos/trafficshaper.py
index f42f4d022..c63c7cf39 100644
--- a/python/vyos/qos/trafficshaper.py
+++ b/python/vyos/qos/trafficshaper.py
@@ -22,6 +22,7 @@ MINQUANTUM = 1000
class TrafficShaper(QoSBase):
_parent = 1
+ qostype = 'shaper'
# https://man7.org/linux/man-pages/man8/tc-htb.8.html
def update(self, config, direction):
@@ -70,7 +71,17 @@ class TrafficShaper(QoSBase):
cls = int(cls)
# bandwidth is a mandatory CLI node
- rate = self._rate_convert(cls_config['bandwidth'])
+ # T5296 if bandwidth 'auto' or 'xx%' get value from config shaper total "bandwidth"
+ # i.e from set shaper test bandwidth '300mbit'
+ # without it, it tries to get value from qos.base /sys/class/net/{self._interface}/speed
+ if cls_config['bandwidth'] == 'auto':
+ rate = self._rate_convert(config['bandwidth'])
+ elif cls_config['bandwidth'].endswith('%'):
+ percent = cls_config['bandwidth'].rstrip('%')
+ rate = self._rate_convert(config['bandwidth']) * int(percent) // 100
+ else:
+ rate = self._rate_convert(cls_config['bandwidth'])
+
burst = cls_config['burst']
quantum = cls_config['codel_quantum']
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index 66044fa52..cf731c881 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -25,22 +25,21 @@ import urllib.parse
from ftplib import FTP
from ftplib import FTP_TLS
-from paramiko import SSHClient
+from paramiko import SSHClient, SSHException
from paramiko import MissingHostKeyPolicy
from requests import Session
from requests.adapters import HTTPAdapter
from requests.packages.urllib3 import PoolManager
-from vyos.util import ask_yes_no
-from vyos.util import begin
-from vyos.util import cmd
-from vyos.util import make_incremental_progressbar
-from vyos.util import make_progressbar
-from vyos.util import print_error
+from vyos.utils.io import ask_yes_no
+from vyos.utils.io import make_incremental_progressbar
+from vyos.utils.io import make_progressbar
+from vyos.utils.io import print_error
+from vyos.utils.misc import begin
+from vyos.utils.process import cmd
from vyos.version import get_version
-
CHUNK_SIZE = 8192
class InteractivePolicy(MissingHostKeyPolicy):
@@ -51,7 +50,7 @@ class InteractivePolicy(MissingHostKeyPolicy):
def missing_host_key(self, client, hostname, key):
print_error(f"Host '{hostname}' not found in known hosts.")
print_error('Fingerprint: ' + key.get_fingerprint().hex())
- if ask_yes_no('Do you wish to continue?'):
+ if sys.stdout.isatty() and ask_yes_no('Do you wish to continue?'):
if client._host_keys_filename\
and ask_yes_no('Do you wish to permanently add this host/key pair to known hosts?'):
client._host_keys.add(hostname, key.get_name(), key)
@@ -97,7 +96,13 @@ def check_storage(path, size):
class FtpC:
- def __init__(self, url, progressbar=False, check_space=False, source_host='', source_port=0):
+ def __init__(self,
+ url,
+ progressbar=False,
+ check_space=False,
+ source_host='',
+ source_port=0,
+ timeout=10):
self.secure = url.scheme == 'ftps'
self.hostname = url.hostname
self.path = url.path
@@ -107,12 +112,15 @@ class FtpC:
self.source = (source_host, source_port)
self.progressbar = progressbar
self.check_space = check_space
+ self.timeout = timeout
def _establish(self):
if self.secure:
- return FTP_TLS(source_address=self.source, context=ssl.create_default_context())
+ return FTP_TLS(source_address=self.source,
+ context=ssl.create_default_context(),
+ timeout=self.timeout)
else:
- return FTP(source_address=self.source)
+ return FTP(source_address=self.source, timeout=self.timeout)
def download(self, location: str):
# Open the file upfront before establishing connection.
@@ -151,7 +159,13 @@ class FtpC:
class SshC:
known_hosts = os.path.expanduser('~/.ssh/known_hosts')
- def __init__(self, url, progressbar=False, check_space=False, source_host='', source_port=0):
+ def __init__(self,
+ url,
+ progressbar=False,
+ check_space=False,
+ source_host='',
+ source_port=0,
+ timeout=10.0):
self.hostname = url.hostname
self.path = url.path
self.username = url.username or os.getenv('REMOTE_USERNAME')
@@ -160,6 +174,7 @@ class SshC:
self.source = (source_host, source_port)
self.progressbar = progressbar
self.check_space = check_space
+ self.timeout = timeout
def _establish(self):
ssh = SSHClient()
@@ -170,7 +185,7 @@ class SshC:
ssh.set_missing_host_key_policy(InteractivePolicy())
# `socket.create_connection()` automatically picks a NIC and an IPv4/IPv6 address family
# for us on dual-stack systems.
- sock = socket.create_connection((self.hostname, self.port), socket.getdefaulttimeout(), self.source)
+ sock = socket.create_connection((self.hostname, self.port), self.timeout, self.source)
ssh.connect(self.hostname, self.port, self.username, self.password, sock=sock)
return ssh
@@ -199,13 +214,20 @@ class SshC:
class HttpC:
- def __init__(self, url, progressbar=False, check_space=False, source_host='', source_port=0):
+ def __init__(self,
+ url,
+ progressbar=False,
+ check_space=False,
+ source_host='',
+ source_port=0,
+ timeout=10.0):
self.urlstring = urllib.parse.urlunsplit(url)
self.progressbar = progressbar
self.check_space = check_space
self.source_pair = (source_host, source_port)
self.username = url.username or os.getenv('REMOTE_USERNAME')
self.password = url.password or os.getenv('REMOTE_PASSWORD')
+ self.timeout = timeout
def _establish(self):
session = Session()
@@ -221,8 +243,11 @@ class HttpC:
# Not only would it potentially mess up with the progress bar but
# `shutil.copyfileobj(request.raw, file)` does not handle automatic decoding.
s.headers.update({'Accept-Encoding': 'identity'})
- with s.head(self.urlstring, allow_redirects=True) as r:
+ with s.head(self.urlstring,
+ allow_redirects=True,
+ timeout=self.timeout) as r:
# Abort early if the destination is inaccessible.
+ print('pre-3')
r.raise_for_status()
# If the request got redirected, keep the last URL we ended up with.
final_urlstring = r.url
@@ -236,7 +261,8 @@ class HttpC:
size = None
if self.check_space:
check_storage(location, size)
- with s.get(final_urlstring, stream=True) as r, open(location, 'wb') as f:
+ with s.get(final_urlstring, stream=True,
+ timeout=self.timeout) as r, open(location, 'wb') as f:
if self.progressbar and size:
progress = make_incremental_progressbar(CHUNK_SIZE / size)
next(progress)
@@ -250,7 +276,10 @@ class HttpC:
def upload(self, location: str):
# Does not yet support progressbars.
with self._establish() as s, open(location, 'rb') as f:
- s.post(self.urlstring, data=f, allow_redirects=True)
+ s.post(self.urlstring,
+ data=f,
+ allow_redirects=True,
+ timeout=self.timeout)
class TftpC:
@@ -259,10 +288,16 @@ class TftpC:
# 2. Since there's no concept authentication, we don't need to deal with keys/passwords.
# 3. It would be a waste to import, audit and maintain a third-party library for TFTP.
# 4. I'd rather not implement the entire protocol here, no matter how simple it is.
- def __init__(self, url, progressbar=False, check_space=False, source_host=None, source_port=0):
+ def __init__(self,
+ url,
+ progressbar=False,
+ check_space=False,
+ source_host=None,
+ source_port=0,
+ timeout=10):
source_option = f'--interface {source_host} --local-port {source_port}' if source_host else ''
progress_flag = '--progress-bar' if progressbar else '-s'
- self.command = f'curl {source_option} {progress_flag}'
+ self.command = f'curl {source_option} {progress_flag} --connect-timeout {timeout}'
self.urlstring = urllib.parse.urlunsplit(url)
def download(self, location: str):
@@ -287,10 +322,16 @@ def urlc(urlstring, *args, **kwargs):
raise ValueError(f'Unsupported URL scheme: "{url.scheme}"')
def download(local_path, urlstring, *args, **kwargs):
- urlc(urlstring, *args, **kwargs).download(local_path)
+ try:
+ urlc(urlstring, *args, **kwargs).download(local_path)
+ except Exception as err:
+ print_error(f'Unable to download "{urlstring}": {err}')
def upload(local_path, urlstring, *args, **kwargs):
- urlc(urlstring, *args, **kwargs).upload(local_path)
+ try:
+ urlc(urlstring, *args, **kwargs).upload(local_path)
+ except Exception as err:
+ print_error(f'Unable to upload "{urlstring}": {err}')
def get_remote_config(urlstring, source_host='', source_port=0):
"""
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 254a15e3a..e167488c6 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -20,10 +20,10 @@ 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
-from vyos.util import dict_search_args
-from vyos.util import makedir
+from vyos.utils.dict import dict_search_args
+from vyos.utils.file import makedir
+from vyos.utils.permission import chmod
+from vyos.utils.permission import chown
# Holds template filters registered via register_filter()
_FILTERS = {}
@@ -162,19 +162,19 @@ def force_to_list(value):
@register_filter('seconds_to_human')
def seconds_to_human(seconds, separator=""):
""" Convert seconds to human-readable values like 1d6h15m23s """
- from vyos.util import seconds_to_human
+ from vyos.utils.convert import seconds_to_human
return seconds_to_human(seconds, separator=separator)
@register_filter('bytes_to_human')
def bytes_to_human(bytes, initial_exponent=0, precision=2):
""" Convert bytes to human-readable values like 1.44M """
- from vyos.util import bytes_to_human
+ from vyos.utils.convert import bytes_to_human
return bytes_to_human(bytes, initial_exponent=initial_exponent, precision=precision)
@register_filter('human_to_bytes')
def human_to_bytes(value):
""" Convert a data amount with a unit suffix to bytes, like 2K to 2048 """
- from vyos.util import human_to_bytes
+ from vyos.utils.convert import human_to_bytes
return human_to_bytes(value)
@register_filter('ip_from_cidr')
@@ -420,11 +420,11 @@ def get_dhcp_router(interface):
Returns False of no router is found, returns the IP address as string if
a router is found.
"""
- lease_file = f'/var/lib/dhcp/dhclient_{interface}.leases'
+ lease_file = directories['isc_dhclient_dir'] + f'/dhclient_{interface}.leases'
if not os.path.exists(lease_file):
return None
- from vyos.util import read_file
+ from vyos.utils.file import read_file
for line in read_file(lease_file).splitlines():
if 'option routers' in line:
(_, _, address) = line.split()
@@ -574,9 +574,9 @@ def nft_action(vyos_action):
return vyos_action
@register_filter('nft_rule')
-def nft_rule(rule_conf, fw_name, rule_id, ip_name='ip'):
+def nft_rule(rule_conf, fw_hook, fw_name, rule_id, ip_name='ip'):
from vyos.firewall import parse_rule
- return parse_rule(rule_conf, fw_name, rule_id, ip_name)
+ return parse_rule(rule_conf, fw_hook, fw_name, rule_id, ip_name)
@register_filter('nft_default_rule')
def nft_default_rule(fw_conf, fw_name, ipv6=False):
@@ -587,7 +587,8 @@ def nft_default_rule(fw_conf, fw_name, ipv6=False):
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(nft_action(default_action))
+ output.append(f'{default_action}')
if 'default_jump_target' in fw_conf:
target = fw_conf['default_jump_target']
def_suffix = '6' if ipv6 else ''
diff --git a/python/vyos/util.py b/python/vyos/util.py
deleted file mode 100644
index d83287fd2..000000000
--- a/python/vyos/util.py
+++ /dev/null
@@ -1,1160 +0,0 @@
-# Copyright 2020-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
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-import re
-import sys
-
-#
-# NOTE: Do not import full classes here, move your import to the function
-# where it is used so it is as local as possible to the execution
-#
-
-from subprocess import Popen
-from subprocess import PIPE
-from subprocess import STDOUT
-from subprocess import DEVNULL
-
-def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=PIPE, stderr=PIPE, decode='utf-8'):
- """
- popen is a wrapper helper aound subprocess.Popen
- with it default setting it will return a tuple (out, err)
- out: the output of the program run
- err: the error code returned by the program
-
- it can be affected by the following flags:
- shell: do not try to auto-detect if a shell is required
- for example if a pipe (|) or redirection (>, >>) is used
- input: data to sent to the child process via STDIN
- the data should be bytes but string will be converted
- timeout: time after which the command will be considered to have failed
- env: mapping that defines the environment variables for the new process
- stdout: define how the output of the program should be handled
- - PIPE (default), sends stdout to the output
- - DEVNULL, discard the output
- stderr: define how the output of the program should be handled
- - None (default), send/merge the data to/with stderr
- - PIPE, popen will append it to output
- - STDOUT, send the data to be merged with stdout
- - DEVNULL, discard the output
- decode: specify the expected text encoding (utf-8, ascii, ...)
- the default is explicitely utf-8 which is python's own default
-
- usage:
- get both stdout and stderr: popen('command', stdout=PIPE, stderr=STDOUT)
- discard stdout and get stderr: popen('command', stdout=DEVNUL, stderr=PIPE)
- """
-
- # airbag must be left as an import in the function as otherwise we have a
- # a circual import dependency
- from vyos import debug
- from vyos import airbag
-
- # log if the flag is set, otherwise log if command is set
- if not debug.enabled(flag):
- flag = 'command'
-
- cmd_msg = f"cmd '{command}'"
- debug.message(cmd_msg, flag)
-
- use_shell = shell
- stdin = None
- if shell is None:
- use_shell = False
- if ' ' in command:
- use_shell = True
- if env:
- use_shell = True
-
- if input:
- stdin = PIPE
- input = input.encode() if type(input) is str else input
-
- p = Popen(command, stdin=stdin, stdout=stdout, stderr=stderr,
- env=env, shell=use_shell)
-
- pipe = p.communicate(input, timeout)
-
- pipe_out = b''
- if stdout == PIPE:
- pipe_out = pipe[0]
-
- pipe_err = b''
- if stderr == PIPE:
- pipe_err = pipe[1]
-
- str_out = pipe_out.decode(decode).replace('\r\n', '\n').strip()
- str_err = pipe_err.decode(decode).replace('\r\n', '\n').strip()
-
- out_msg = f"returned (out):\n{str_out}"
- if str_out:
- debug.message(out_msg, flag)
-
- if str_err:
- err_msg = f"returned (err):\n{str_err}"
- # this message will also be send to syslog via airbag
- debug.message(err_msg, flag, destination=sys.stderr)
-
- # should something go wrong, report this too via airbag
- airbag.noteworthy(cmd_msg)
- airbag.noteworthy(out_msg)
- airbag.noteworthy(err_msg)
-
- return str_out, p.returncode
-
-
-def run(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=DEVNULL, stderr=PIPE, decode='utf-8'):
- """
- A wrapper around popen, which discard the stdout and
- will return the error code of a command
- """
- _, code = popen(
- command, flag,
- stdout=stdout, stderr=stderr,
- input=input, timeout=timeout,
- env=env, shell=shell,
- decode=decode,
- )
- return code
-
-
-def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=PIPE, stderr=PIPE, decode='utf-8', raising=None, message='',
- expect=[0]):
- """
- A wrapper around popen, which returns the stdout and
- will raise the error code of a command
-
- raising: specify which call should be used when raising
- the class should only require a string as parameter
- (default is OSError) with the error code
- expect: a list of error codes to consider as normal
- """
- decoded, code = popen(
- command, flag,
- stdout=stdout, stderr=stderr,
- input=input, timeout=timeout,
- env=env, shell=shell,
- decode=decode,
- )
- if code not in expect:
- feedback = message + '\n' if message else ''
- feedback += f'failed to run command: {command}\n'
- feedback += f'returned: {decoded}\n'
- feedback += f'exit code: {code}'
- if raising is None:
- # error code can be recovered with .errno
- raise OSError(code, feedback)
- else:
- raise raising(feedback)
- return decoded
-
-
-def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=PIPE, stderr=STDOUT, decode='utf-8'):
- """
- A wrapper around popen, which returns the return code
- of a command and stdout
-
- % rc_cmd('uname')
- (0, 'Linux')
- % rc_cmd('ip link show dev eth99')
- (1, 'Device "eth99" does not exist.')
- """
- out, code = popen(
- command, flag,
- stdout=stdout, stderr=stderr,
- input=input, timeout=timeout,
- env=env, shell=shell,
- decode=decode,
- )
- return code, out
-
-
-def call(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=PIPE, stderr=PIPE, decode='utf-8'):
- """
- A wrapper around popen, which print the stdout and
- will return the error code of a command
- """
- out, code = popen(
- command, flag,
- stdout=stdout, stderr=stderr,
- input=input, timeout=timeout,
- env=env, shell=shell,
- decode=decode,
- )
- if out:
- print(out)
- return code
-
-
-def read_file(fname, defaultonfailure=None):
- """
- read the content of a file, stripping any end characters (space, newlines)
- should defaultonfailure be not None, it is returned on failure to read
- """
- try:
- """ Read a file to string """
- with open(fname, 'r') as f:
- data = f.read().strip()
- return data
- except Exception as e:
- if defaultonfailure is not None:
- return defaultonfailure
- raise e
-
-def write_file(fname, data, defaultonfailure=None, user=None, group=None, mode=None, append=False):
- """
- Write content of data to given fname, should defaultonfailure be not None,
- it is returned on failure to read.
-
- If directory of file is not present, it is auto-created.
- """
- dirname = os.path.dirname(fname)
- if not os.path.isdir(dirname):
- os.makedirs(dirname, mode=0o755, exist_ok=False)
- chown(dirname, user, group)
-
- try:
- """ Write a file to string """
- bytes = 0
- with open(fname, 'w' if not append else 'a') as f:
- bytes = f.write(data)
- chown(fname, user, group)
- chmod(fname, mode)
- return bytes
- except Exception as e:
- if defaultonfailure is not None:
- return defaultonfailure
- raise e
-
-def read_json(fname, defaultonfailure=None):
- """
- read and json decode the content of a file
- should defaultonfailure be not None, it is returned on failure to read
- """
- import json
- try:
- with open(fname, 'r') as f:
- data = json.load(f)
- return data
- except Exception as e:
- if defaultonfailure is not None:
- return defaultonfailure
- raise e
-
-
-def chown(path, user, group):
- """ change file/directory owner """
- from pwd import getpwnam
- from grp import getgrnam
-
- if user is None or group is None:
- return False
-
- # path may also be an open file descriptor
- if not isinstance(path, int) and not os.path.exists(path):
- return False
-
- uid = getpwnam(user).pw_uid
- gid = getgrnam(group).gr_gid
- os.chown(path, uid, gid)
- return True
-
-
-def chmod(path, bitmask):
- # path may also be an open file descriptor
- if not isinstance(path, int) and not os.path.exists(path):
- return
- if bitmask is None:
- return
- os.chmod(path, bitmask)
-
-
-def chmod_600(path):
- """ make file only read/writable by owner """
- from stat import S_IRUSR, S_IWUSR
-
- bitmask = S_IRUSR | S_IWUSR
- chmod(path, bitmask)
-
-
-def chmod_750(path):
- """ make file/directory only executable to user and group """
- from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP
-
- bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
- chmod(path, bitmask)
-
-
-def chmod_755(path):
- """ make file executable by all """
- from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
-
- bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | \
- S_IROTH | S_IXOTH
- chmod(path, bitmask)
-
-
-def makedir(path, user=None, group=None):
- if os.path.exists(path):
- return
- os.makedirs(path, mode=0o755)
- chown(path, user, group)
-
-def colon_separated_to_dict(data_string, uniquekeys=False):
- """ Converts a string containing newline-separated entries
- of colon-separated key-value pairs into a dict.
-
- Such files are common in Linux /proc filesystem
-
- Args:
- data_string (str): data string
- uniquekeys (bool): whether to insist that keys are unique or not
-
- Returns: dict
-
- Raises:
- ValueError: if uniquekeys=True and the data string has
- duplicate keys.
-
- Note:
- If uniquekeys=True, then dict entries are always strings,
- otherwise they are always lists of strings.
- """
- import re
- key_value_re = re.compile('([^:]+)\s*\:\s*(.*)')
-
- data_raw = re.split('\n', data_string)
-
- data = {}
-
- for l in data_raw:
- l = l.strip()
- if l:
- match = re.match(key_value_re, l)
- if match and (len(match.groups()) == 2):
- key = match.groups()[0].strip()
- value = match.groups()[1].strip()
- else:
- raise ValueError(f"""Line "{l}" could not be parsed a colon-separated pair """, l)
- if key in data.keys():
- if uniquekeys:
- raise ValueError("Data string has duplicate keys: {0}".format(key))
- else:
- data[key].append(value)
- else:
- if uniquekeys:
- data[key] = value
- else:
- data[key] = [value]
- else:
- pass
-
- return data
-
-def _mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_mangle=False, mod=0):
- """ Mangles dict keys according to a regex and replacement character.
- Some libraries like Jinja2 do not like certain characters in dict keys.
- This function can be used for replacing all offending characters
- with something acceptable.
-
- Args:
- data (dict): Original dict to mangle
-
- Returns: dict
- """
- from vyos.xml import is_tag
-
- new_dict = {}
-
- for key in data.keys():
- save_mod = mod
- save_path = abs_path[:]
-
- abs_path.append(key)
-
- if not is_tag(abs_path):
- new_key = re.sub(regex, replacement, key)
- else:
- if mod%2:
- new_key = key
- else:
- new_key = re.sub(regex, replacement, key)
- if no_tag_node_value_mangle:
- mod += 1
-
- value = data[key]
-
- if isinstance(value, dict):
- new_dict[new_key] = _mangle_dict_keys(value, regex, replacement, abs_path=abs_path, mod=mod, no_tag_node_value_mangle=no_tag_node_value_mangle)
- else:
- new_dict[new_key] = value
-
- mod = save_mod
- abs_path = save_path[:]
-
- return new_dict
-
-def mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_mangle=False):
- return _mangle_dict_keys(data, regex, replacement, abs_path=abs_path, no_tag_node_value_mangle=no_tag_node_value_mangle, mod=0)
-
-def _get_sub_dict(d, lpath):
- k = lpath[0]
- if k not in d.keys():
- return {}
- c = {k: d[k]}
- lpath = lpath[1:]
- if not lpath:
- return c
- elif not isinstance(c[k], dict):
- return {}
- return _get_sub_dict(c[k], lpath)
-
-def get_sub_dict(source, lpath, get_first_key=False):
- """ Returns the sub-dict of a nested dict, defined by path of keys.
-
- Args:
- source (dict): Source dict to extract from
- lpath (list[str]): sequence of keys
-
- Returns: source, if lpath is empty, else
- {key : source[..]..[key]} for key the last element of lpath, if exists
- {} otherwise
- """
- if not isinstance(source, dict):
- raise TypeError("source must be of type dict")
- if not isinstance(lpath, list):
- raise TypeError("path must be of type list")
- if not lpath:
- return source
-
- ret = _get_sub_dict(source, lpath)
-
- if get_first_key and lpath and ret:
- tmp = next(iter(ret.values()))
- if not isinstance(tmp, dict):
- raise TypeError("Data under node is not of type dict")
- ret = tmp
-
- return ret
-
-def process_running(pid_file):
- """ Checks if a process with PID in pid_file is running """
- from psutil import pid_exists
- if not os.path.isfile(pid_file):
- return False
- with open(pid_file, 'r') as f:
- pid = f.read().strip()
- return pid_exists(int(pid))
-
-def process_named_running(name, cmdline: str=None):
- """ Checks if process with given name is running and returns its PID.
- If Process is not running, return None
- """
- from psutil import process_iter
- for p in process_iter(['name', 'pid', 'cmdline']):
- if cmdline:
- if p.info['name'] == name and cmdline in p.info['cmdline']:
- return p.info['pid']
- elif p.info['name'] == name:
- return p.info['pid']
- return None
-
-def is_list_equal(first: list, second: list) -> bool:
- """ Check if 2 lists are equal and list not empty """
- if len(first) != len(second) or len(first) == 0:
- return False
- return sorted(first) == sorted(second)
-
-def is_listen_port_bind_service(port: int, service: str) -> bool:
- """Check if listen port bound to expected program name
- :param port: Bind port
- :param service: Program name
- :return: bool
-
- Example:
- % is_listen_port_bind_service(443, 'nginx')
- True
- % is_listen_port_bind_service(443, 'ocserv-main')
- False
- """
- from psutil import net_connections as connections
- from psutil import Process as process
- for connection in connections():
- addr = connection.laddr
- pid = connection.pid
- pid_name = process(pid).name()
- pid_port = addr.port
- if service == pid_name and port == pid_port:
- return True
- return False
-
-def seconds_to_human(s, separator=""):
- """ Converts number of seconds passed to a human-readable
- interval such as 1w4d18h35m59s
- """
- s = int(s)
-
- week = 60 * 60 * 24 * 7
- day = 60 * 60 * 24
- hour = 60 * 60
-
- remainder = 0
- result = ""
-
- weeks = s // week
- if weeks > 0:
- result = "{0}w".format(weeks)
- s = s % week
-
- days = s // day
- if days > 0:
- result = "{0}{1}{2}d".format(result, separator, days)
- s = s % day
-
- hours = s // hour
- if hours > 0:
- result = "{0}{1}{2}h".format(result, separator, hours)
- s = s % hour
-
- minutes = s // 60
- if minutes > 0:
- result = "{0}{1}{2}m".format(result, separator, minutes)
- s = s % 60
-
- seconds = s
- if seconds > 0:
- result = "{0}{1}{2}s".format(result, separator, seconds)
-
- return result
-
-def bytes_to_human(bytes, initial_exponent=0, precision=2):
- """ Converts a value in bytes to a human-readable size string like 640 KB
-
- The initial_exponent parameter is the exponent of 2,
- e.g. 10 (1024) for kilobytes, 20 (1024 * 1024) for megabytes.
- """
-
- if bytes == 0:
- return "0 B"
-
- from math import log2
-
- bytes = bytes * (2**initial_exponent)
-
- # log2 is a float, while range checking requires an int
- exponent = int(log2(bytes))
-
- if exponent < 10:
- value = bytes
- suffix = "B"
- elif exponent in range(10, 20):
- value = bytes / 1024
- suffix = "KB"
- elif exponent in range(20, 30):
- value = bytes / 1024**2
- suffix = "MB"
- elif exponent in range(30, 40):
- value = bytes / 1024**3
- suffix = "GB"
- else:
- value = bytes / 1024**4
- suffix = "TB"
- # Add a new case when the first machine with petabyte RAM
- # hits the market.
-
- size_string = "{0:.{1}f} {2}".format(value, precision, suffix)
- return size_string
-
-def human_to_bytes(value):
- """ Converts a data amount with a unit suffix to bytes, like 2K to 2048 """
-
- from re import match as re_match
-
- res = re_match(r'^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$', value)
-
- if not res:
- raise ValueError(f"'{value}' is not a valid data amount")
- else:
- amount = float(res.group(1))
- unit = res.group(2).lower()
-
- if unit == 'b':
- res = amount
- elif (unit == 'k') or (unit == 'kb'):
- res = amount * 1024
- elif (unit == 'm') or (unit == 'mb'):
- res = amount * 1024**2
- elif (unit == 'g') or (unit == 'gb'):
- res = amount * 1024**3
- elif (unit == 't') or (unit == 'tb'):
- res = amount * 1024**4
- else:
- raise ValueError(f"Unsupported data unit '{unit}'")
-
- # There cannot be fractional bytes, so we convert them to integer.
- # However, truncating causes problems with conversion back to human unit,
- # so we round instead -- that seems to work well enough.
- return round(res)
-
-def get_cfg_group_id():
- from grp import getgrnam
- from vyos.defaults import cfg_group
-
- group_data = getgrnam(cfg_group)
- return group_data.gr_gid
-
-
-def file_is_persistent(path):
- import re
- location = r'^(/config|/opt/vyatta/etc/config)'
- absolute = os.path.abspath(os.path.dirname(path))
- return re.match(location,absolute)
-
-def wait_for_inotify(file_path, pre_hook=None, event_type=None, timeout=None, sleep_interval=0.1):
- """ Waits for an inotify event to occur """
- if not os.path.dirname(file_path):
- raise ValueError(
- "File path {} does not have a directory part (required for inotify watching)".format(file_path))
- if not os.path.basename(file_path):
- raise ValueError(
- "File path {} does not have a file part, do not know what to watch for".format(file_path))
-
- from inotify.adapters import Inotify
- from time import time
- from time import sleep
-
- time_start = time()
-
- i = Inotify()
- i.add_watch(os.path.dirname(file_path))
-
- if pre_hook:
- pre_hook()
-
- for event in i.event_gen(yield_nones=True):
- if (timeout is not None) and ((time() - time_start) > timeout):
- # If the function didn't return until this point,
- # the file failed to have been written to and closed within the timeout
- raise OSError("Waiting for file {} to be written has failed".format(file_path))
-
- # Most such events don't take much time, so it's better to check right away
- # and sleep later.
- if event is not None:
- (_, type_names, path, filename) = event
- if filename == os.path.basename(file_path):
- if event_type in type_names:
- return
- sleep(sleep_interval)
-
-def wait_for_file_write_complete(file_path, pre_hook=None, timeout=None, sleep_interval=0.1):
- """ Waits for a process to close a file after opening it in write mode. """
- wait_for_inotify(file_path,
- event_type='IN_CLOSE_WRITE', pre_hook=pre_hook, timeout=timeout, sleep_interval=sleep_interval)
-
-def commit_in_progress():
- """ Not to be used in normal op mode scripts! """
-
- # The CStore backend locks the config by opening a file
- # The file is not removed after commit, so just checking
- # if it exists is insufficient, we need to know if it's open by anyone
-
- # There are two ways to check if any other process keeps a file open.
- # The first one is to try opening it and see if the OS objects.
- # That's faster but prone to race conditions and can be intrusive.
- # The other one is to actually check if any process keeps it open.
- # It's non-intrusive but needs root permissions, else you can't check
- # processes of other users.
- #
- # Since this will be used in scripts that modify the config outside of the CLI
- # framework, those knowingly have root permissions.
- # For everything else, we add a safeguard.
- from psutil import process_iter
- from psutil import NoSuchProcess
- from getpass import getuser
- from vyos.defaults import commit_lock
-
- if getuser() != 'root':
- raise OSError('This functions needs to be run as root to return correct results!')
-
- for proc in process_iter():
- try:
- files = proc.open_files()
- if files:
- for f in files:
- if f.path == commit_lock:
- return True
- except NoSuchProcess as err:
- # Process died before we could examine it
- pass
- # Default case
- return False
-
-
-def wait_for_commit_lock():
- """ Not to be used in normal op mode scripts! """
- from time import sleep
- # Very synchronous approach to multiprocessing
- while commit_in_progress():
- sleep(1)
-
-def ask_input(question, default='', numeric_only=False, valid_responses=[]):
- question_out = question
- if default:
- question_out += f' (Default: {default})'
- response = ''
- while True:
- response = input(question_out + ' ').strip()
- if not response and default:
- return default
- if numeric_only:
- if not response.isnumeric():
- print("Invalid value, try again.")
- continue
- response = int(response)
- if valid_responses and response not in valid_responses:
- print("Invalid value, try again.")
- continue
- break
- return response
-
-def ask_yes_no(question, default=False) -> bool:
- """Ask a yes/no question via input() and return their answer."""
- from sys import stdout
- default_msg = "[Y/n]" if default else "[y/N]"
- while True:
- try:
- stdout.write("%s %s " % (question, default_msg))
- c = input().lower()
- if c == '':
- return default
- elif c in ("y", "ye", "yes"):
- return True
- elif c in ("n", "no"):
- return False
- else:
- stdout.write("Please respond with yes/y or no/n\n")
- except EOFError:
- stdout.write("\nPlease respond with yes/y or no/n\n")
-
-def is_admin() -> bool:
- """Look if current user is in sudo group"""
- from getpass import getuser
- from grp import getgrnam
- current_user = getuser()
- (_, _, _, admin_group_members) = getgrnam('sudo')
- return current_user in admin_group_members
-
-
-def mac2eui64(mac, prefix=None):
- """
- Convert a MAC address to a EUI64 address or, with prefix provided, a full
- IPv6 address.
- Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3
- """
- import re
- from ipaddress import ip_network
- # http://tools.ietf.org/html/rfc4291#section-2.5.1
- eui64 = re.sub(r'[.:-]', '', mac).lower()
- eui64 = eui64[0:6] + 'fffe' + eui64[6:]
- eui64 = hex(int(eui64[0:2], 16) ^ 2)[2:].zfill(2) + eui64[2:]
-
- if prefix is None:
- return ':'.join(re.findall(r'.{4}', eui64))
- else:
- try:
- net = ip_network(prefix, strict=False)
- euil = int('0x{0}'.format(eui64), 16)
- return str(net[euil])
- except: # pylint: disable=bare-except
- return
-
-def get_half_cpus():
- """ return 1/2 of the numbers of available CPUs """
- cpu = os.cpu_count()
- if cpu > 1:
- cpu /= 2
- return int(cpu)
-
-def check_kmod(k_mod):
- """ Common utility function to load required kernel modules on demand """
- from vyos import ConfigError
- if isinstance(k_mod, str):
- k_mod = k_mod.split()
- for module in k_mod:
- if not os.path.exists(f'/sys/module/{module}'):
- if call(f'modprobe {module}') != 0:
- raise ConfigError(f'Loading Kernel module {module} failed')
-
-def find_device_file(device):
- """ Recurively search /dev for the given device file and return its full path.
- If no device file was found 'None' is returned """
- from fnmatch import fnmatch
-
- for root, dirs, files in os.walk('/dev'):
- for basename in files:
- if fnmatch(basename, device):
- return os.path.join(root, basename)
-
- return None
-
-def dict_search(path, dict_object):
- """ Traverse Python dictionary (dict_object) delimited by dot (.).
- Return value of key if found, None otherwise.
-
- This is faster implementation then jmespath.search('foo.bar', dict_object)"""
- if not isinstance(dict_object, dict) or not path:
- return None
-
- parts = path.split('.')
- inside = parts[:-1]
- if not inside:
- if path not in dict_object:
- return None
- return dict_object[path]
- c = dict_object
- for p in parts[:-1]:
- c = c.get(p, {})
- return c.get(parts[-1], None)
-
-def dict_search_args(dict_object, *path):
- # Traverse dictionary using variable arguments
- # Added due to above function not allowing for '.' in the key names
- # Example: dict_search_args(some_dict, 'key', 'subkey', 'subsubkey', ...)
- if not isinstance(dict_object, dict) or not path:
- return None
-
- for item in path:
- if item not in dict_object:
- return None
- dict_object = dict_object[item]
- return dict_object
-
-def dict_search_recursive(dict_object, key, path=[]):
- """ Traverse a dictionary recurisvely and return the value of the key
- we are looking for.
-
- Thankfully copied from https://stackoverflow.com/a/19871956
-
- Modified to yield optional path to found keys
- """
- if isinstance(dict_object, list):
- for i in dict_object:
- new_path = path + [i]
- for x in dict_search_recursive(i, key, new_path):
- yield x
- elif isinstance(dict_object, dict):
- if key in dict_object:
- new_path = path + [key]
- yield dict_object[key], new_path
- for k, j in dict_object.items():
- new_path = path + [k]
- for x in dict_search_recursive(j, key, new_path):
- yield x
-
-def convert_data(data):
- """Convert multiple types of data to types usable in CLI
-
- Args:
- data (str | bytes | list | OrderedDict): input data
-
- Returns:
- str | list | dict: converted data
- """
- from base64 import b64encode
- from collections import OrderedDict
-
- if isinstance(data, str):
- return data
- if isinstance(data, bytes):
- try:
- return data.decode()
- except UnicodeDecodeError:
- return b64encode(data).decode()
- if isinstance(data, list):
- list_tmp = []
- for item in data:
- list_tmp.append(convert_data(item))
- return list_tmp
- if isinstance(data, OrderedDict):
- dict_tmp = {}
- for key, value in data.items():
- dict_tmp[key] = convert_data(value)
- return dict_tmp
-
-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, netns=None):
- """ Returns the used encapsulation protocol for given interface.
- If interface does not exist, None is returned.
- """
- from vyos.util import run
- netns_exec = f'ip netns exec {netns}' if netns else ''
- if run(f'{netns_exec} test -e /sys/class/net/{interface}') != 0:
- return None
- from json import loads
- tmp = loads(cmd(f'{netns_exec} ip -d -j link show {interface}'))[0]
- return tmp
-
-def get_interface_address(interface):
- """ Returns the used encapsulation protocol for given interface.
- If interface does not exist, None is returned.
- """
- if not os.path.exists(f'/sys/class/net/{interface}'):
- return None
- from json import loads
- tmp = loads(cmd(f'ip -d -j addr show {interface}'))[0]
- return tmp
-
-def get_interface_namespace(iface):
- """
- Returns wich netns the interface belongs to
- """
- from json import loads
- # Check if netns exist
- tmp = loads(cmd(f'ip --json netns ls'))
- if len(tmp) == 0:
- return None
-
- for ns in tmp:
- namespace = f'{ns["name"]}'
- # Search interface in each netns
- data = loads(cmd(f'ip netns exec {namespace} ip -j link show'))
- for compare in data:
- if iface == compare["ifname"]:
- return namespace
-
-def get_all_vrfs():
- """ Return a dictionary of all system wide known VRF instances """
- from json import loads
- tmp = loads(cmd('ip -j vrf list'))
- # Result is of type [{"name":"red","table":1000},{"name":"blue","table":2000}]
- # so we will re-arrange it to a more nicer representation:
- # {'red': {'table': 1000}, 'blue': {'table': 2000}}
- data = {}
- for entry in tmp:
- name = entry.pop('name')
- data[name] = entry
- return data
-
-def print_error(str='', end='\n'):
- """
- Print `str` to stderr, terminated with `end`.
- Used for warnings and out-of-band messages to avoid mangling precious
- stdout output.
- """
- sys.stderr.write(str)
- sys.stderr.write(end)
- sys.stderr.flush()
-
-def make_progressbar():
- """
- Make a procedure that takes two arguments `done` and `total` and prints a
- progressbar based on the ratio thereof, whose length is determined by the
- width of the terminal.
- """
- import shutil, math
- col, _ = shutil.get_terminal_size()
- col = max(col - 15, 20)
- def print_progressbar(done, total):
- if done <= total:
- increment = total / col
- length = math.ceil(done / increment)
- percentage = str(math.ceil(100 * done / total)).rjust(3)
- print_error(f'[{length * "#"}{(col - length) * "_"}] {percentage}%', '\r')
- # Print a newline so that the subsequent prints don't overwrite the full bar.
- if done == total:
- print_error()
- return print_progressbar
-
-def make_incremental_progressbar(increment: float):
- """
- Make a generator that displays a progressbar that grows monotonically with
- every iteration.
- First call displays it at 0% and every subsequent iteration displays it
- at `increment` increments where 0.0 < `increment` < 1.0.
- Intended for FTP and HTTP transfers with stateless callbacks.
- """
- print_progressbar = make_progressbar()
- total = 0.0
- while total < 1.0:
- print_progressbar(total, 1.0)
- yield
- total += increment
- print_progressbar(1, 1)
- # Ignore further calls.
- while True:
- yield
-
-def begin(*args):
- """
- Evaluate arguments in order and return the result of the *last* argument.
- For combining multiple expressions in one statement. Useful for lambdas.
- """
- return args[-1]
-
-def begin0(*args):
- """
- Evaluate arguments in order and return the result of the *first* argument.
- For combining multiple expressions in one statement. Useful for lambdas.
- """
- return args[0]
-
-def is_systemd_service_active(service):
- """ Test is a specified systemd service is activated.
- Returns True if service is active, false otherwise.
- Copied from: https://unix.stackexchange.com/a/435317 """
- tmp = cmd(f'systemctl show --value -p ActiveState {service}')
- return bool((tmp == 'active'))
-
-def is_systemd_service_running(service):
- """ Test is a specified systemd service is actually running.
- Returns True if service is running, false otherwise.
- Copied from: https://unix.stackexchange.com/a/435317 """
- tmp = cmd(f'systemctl show --value -p SubState {service}')
- return bool((tmp == 'running'))
-
-def check_port_availability(ipaddress, port, protocol):
- """
- Check if port is available and not used by any service
- Return False if a port is busy or IP address does not exists
- Should be used carefully for services that can start listening
- dynamically, because IP address may be dynamic too
- """
- from socketserver import TCPServer, UDPServer
- from ipaddress import ip_address
-
- # verify arguments
- try:
- ipaddress = ip_address(ipaddress).compressed
- except:
- raise ValueError(f'The {ipaddress} is not a valid IPv4 or IPv6 address')
- if port not in range(1, 65536):
- raise ValueError(f'The port number {port} is not in the 1-65535 range')
- if protocol not in ['tcp', 'udp']:
- raise ValueError(
- f'The protocol {protocol} is not supported. Only tcp and udp are allowed'
- )
-
- # check port availability
- try:
- if protocol == 'tcp':
- server = TCPServer((ipaddress, port), None, bind_and_activate=True)
- if protocol == 'udp':
- server = UDPServer((ipaddress, port), None, bind_and_activate=True)
- server.server_close()
- return True
- except:
- return False
-
-def install_into_config(conf, config_paths, override_prompt=True):
- # Allows op-mode scripts to install values if called from an active config session
- # config_paths: dict of config paths
- # override_prompt: if True, user will be prompted before existing nodes are overwritten
-
- if not config_paths:
- return None
-
- from vyos.config import Config
-
- if not Config().in_session():
- print('You are not in configure mode, commands to install manually from configure mode:')
- for path in config_paths:
- print(f'set {path}')
- 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
-
- 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.')
-
-def is_wwan_connected(interface):
- """ Determine if a given WWAN interface, e.g. wwan0 is connected to the
- carrier network or not """
- import json
-
- if not interface.startswith('wwan'):
- raise ValueError(f'Specified interface "{interface}" is not a WWAN interface')
-
- # 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')
- tmp = json.loads(tmp)
-
- # return True/False if interface is in connected state
- return dict_search('modem.generic.state', tmp) == 'connected'
-
-def boot_configuration_complete() -> bool:
- """ Check if the boot config loader has completed
- """
- from vyos.defaults import config_status
-
- 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
-
-def load_as_module(name: str, path: str):
- import importlib.util
-
- spec = importlib.util.spec_from_file_location(name, path)
- mod = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(mod)
- return mod
diff --git a/python/vyos/utils/__init__.py b/python/vyos/utils/__init__.py
index e69de29bb..12ef2d3b8 100644
--- a/python/vyos/utils/__init__.py
+++ b/python/vyos/utils/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from vyos.utils import assertion
+from vyos.utils import auth
+from vyos.utils import boot
+from vyos.utils import commit
+from vyos.utils import convert
+from vyos.utils import dict
+from vyos.utils import file
+from vyos.utils import io
+from vyos.utils import kernel
+from vyos.utils import list
+from vyos.utils import misc
+from vyos.utils import network
+from vyos.utils import permission
+from vyos.utils import process
+from vyos.utils import system
diff --git a/python/vyos/utils/assertion.py b/python/vyos/utils/assertion.py
new file mode 100644
index 000000000..1aaa54dff
--- /dev/null
+++ b/python/vyos/utils/assertion.py
@@ -0,0 +1,81 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+def assert_boolean(b):
+ if int(b) not in (0, 1):
+ raise ValueError(f'Value {b} out of range')
+
+def assert_range(value, lower=0, count=3):
+ if int(value, 16) not in range(lower, lower+count):
+ raise ValueError("Value out of range")
+
+def assert_list(s, l):
+ if s not in l:
+ o = ' or '.join([f'"{n}"' for n in l])
+ raise ValueError(f'state must be {o}, got {s}')
+
+def assert_number(n):
+ if not str(n).isnumeric():
+ raise ValueError(f'{n} must be a number')
+
+def assert_positive(n, smaller=0):
+ assert_number(n)
+ if int(n) < smaller:
+ raise ValueError(f'{n} is smaller than {smaller}')
+
+def assert_mtu(mtu, ifname):
+ assert_number(mtu)
+
+ import json
+ from vyos.utils.process import cmd
+ out = cmd(f'ip -j -d link show dev {ifname}')
+ # [{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","linkmode":"DEFAULT","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:d9:5b:04","broadcast":"ff:ff:ff:ff:ff:ff","promiscuity":0,"min_mtu":46,"max_mtu":16110,"inet6_addr_gen_mode":"none","num_tx_queues":1,"num_rx_queues":1,"gso_max_size":65536,"gso_max_segs":65535}]
+ parsed = json.loads(out)[0]
+ min_mtu = int(parsed.get('min_mtu', '0'))
+ # cur_mtu = parsed.get('mtu',0),
+ max_mtu = int(parsed.get('max_mtu', '0'))
+ cur_mtu = int(mtu)
+
+ if (min_mtu and cur_mtu < min_mtu) or cur_mtu < 68:
+ raise ValueError(f'MTU is too small for interface "{ifname}": {mtu} < {min_mtu}')
+ if (max_mtu and cur_mtu > max_mtu) or cur_mtu > 65536:
+ raise ValueError(f'MTU is too small for interface "{ifname}": {mtu} > {max_mtu}')
+
+def assert_mac(m):
+ split = m.split(':')
+ size = len(split)
+
+ # a mac address consits out of 6 octets
+ if size != 6:
+ raise ValueError(f'wrong number of MAC octets ({size}): {m}')
+
+ octets = []
+ try:
+ for octet in split:
+ octets.append(int(octet, 16))
+ except ValueError:
+ raise ValueError(f'invalid hex number "{octet}" in : {m}')
+
+ # validate against the first mac address byte if it's a multicast
+ # address
+ if octets[0] & 1:
+ raise ValueError(f'{m} is a multicast MAC address')
+
+ # overall mac address is not allowed to be 00:00:00:00:00:00
+ if sum(octets) == 0:
+ raise ValueError('00:00:00:00:00:00 is not a valid MAC address')
+
+ if octets[:5] == (0, 0, 94, 0, 1):
+ raise ValueError(f'{m} is a VRRP MAC address')
diff --git a/python/vyos/authutils.py b/python/vyos/utils/auth.py
index 66b5f4a74..a59858d72 100644
--- a/python/vyos/authutils.py
+++ b/python/vyos/utils/auth.py
@@ -15,7 +15,7 @@
import re
-from vyos.util import cmd
+from vyos.utils.process import cmd
def make_password_hash(password):
diff --git a/python/vyos/utils/boot.py b/python/vyos/utils/boot.py
new file mode 100644
index 000000000..3aecbec64
--- /dev/null
+++ b/python/vyos/utils/boot.py
@@ -0,0 +1,35 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+def boot_configuration_complete() -> bool:
+ """ Check if the boot config loader has completed
+ """
+ from vyos.defaults import config_status
+ if os.path.isfile(config_status):
+ return True
+ return False
+
+def boot_configuration_success() -> bool:
+ from vyos.defaults import config_status
+ try:
+ with open(config_status) as f:
+ res = f.read().strip()
+ except FileNotFoundError:
+ return False
+ if int(res) == 0:
+ return True
+ return False
diff --git a/python/vyos/utils/commit.py b/python/vyos/utils/commit.py
new file mode 100644
index 000000000..105aed8c2
--- /dev/null
+++ b/python/vyos/utils/commit.py
@@ -0,0 +1,60 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+def commit_in_progress():
+ """ Not to be used in normal op mode scripts! """
+
+ # The CStore backend locks the config by opening a file
+ # The file is not removed after commit, so just checking
+ # if it exists is insufficient, we need to know if it's open by anyone
+
+ # There are two ways to check if any other process keeps a file open.
+ # The first one is to try opening it and see if the OS objects.
+ # That's faster but prone to race conditions and can be intrusive.
+ # The other one is to actually check if any process keeps it open.
+ # It's non-intrusive but needs root permissions, else you can't check
+ # processes of other users.
+ #
+ # Since this will be used in scripts that modify the config outside of the CLI
+ # framework, those knowingly have root permissions.
+ # For everything else, we add a safeguard.
+ from psutil import process_iter
+ from psutil import NoSuchProcess
+ from getpass import getuser
+ from vyos.defaults import commit_lock
+
+ if getuser() != 'root':
+ raise OSError('This functions needs to be run as root to return correct results!')
+
+ for proc in process_iter():
+ try:
+ files = proc.open_files()
+ if files:
+ for f in files:
+ if f.path == commit_lock:
+ return True
+ except NoSuchProcess as err:
+ # Process died before we could examine it
+ pass
+ # Default case
+ return False
+
+
+def wait_for_commit_lock():
+ """ Not to be used in normal op mode scripts! """
+ from time import sleep
+ # Very synchronous approach to multiprocessing
+ while commit_in_progress():
+ sleep(1)
diff --git a/python/vyos/utils/convert.py b/python/vyos/utils/convert.py
index 975c67e0a..9a8a1ff7d 100644
--- a/python/vyos/utils/convert.py
+++ b/python/vyos/utils/convert.py
@@ -143,3 +143,55 @@ def mac_to_eui64(mac, prefix=None):
return str(net[euil])
except: # pylint: disable=bare-except
return
+
+
+def convert_data(data) -> dict | list | tuple | str | int | float | bool | None:
+ """Filter and convert multiple types of data to types usable in CLI/API
+
+ WARNING: Must not be used for anything except formatting output for API or CLI
+
+ On the output allowed everything supported in JSON.
+
+ Args:
+ data (Any): input data
+
+ Returns:
+ dict | list | tuple | str | int | float | bool | None: converted data
+ """
+ from base64 import b64encode
+
+ # return original data for types which do not require conversion
+ if isinstance(data, str | int | float | bool | None):
+ return data
+
+ if isinstance(data, list):
+ list_tmp = []
+ for item in data:
+ list_tmp.append(convert_data(item))
+ return list_tmp
+
+ if isinstance(data, tuple):
+ list_tmp = list(data)
+ tuple_tmp = tuple(convert_data(list_tmp))
+ return tuple_tmp
+
+ if isinstance(data, bytes | bytearray):
+ try:
+ return data.decode()
+ except UnicodeDecodeError:
+ return b64encode(data).decode()
+
+ if isinstance(data, set | frozenset):
+ list_tmp = convert_data(list(data))
+ return list_tmp
+
+ if isinstance(data, dict):
+ dict_tmp = {}
+ for key, value in data.items():
+ dict_tmp[key] = convert_data(value)
+ return dict_tmp
+
+ # do not return anything for other types
+ # which cannot be converted to JSON
+ # for example: complex | range | memoryview
+ return
diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py
index 7c93deef6..9484eacdd 100644
--- a/python/vyos/utils/dict.py
+++ b/python/vyos/utils/dict.py
@@ -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/>.
-
def colon_separated_to_dict(data_string, uniquekeys=False):
""" Converts a string containing newline-separated entries
of colon-separated key-value pairs into a dict.
@@ -65,7 +64,7 @@ def colon_separated_to_dict(data_string, uniquekeys=False):
return data
-def _mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_mangle=False, mod=0):
+def mangle_dict_keys(data, regex, replacement, abs_path=None, no_tag_node_value_mangle=False):
""" Mangles dict keys according to a regex and replacement character.
Some libraries like Jinja2 do not like certain characters in dict keys.
This function can be used for replacing all offending characters
@@ -73,44 +72,39 @@ def _mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_m
Args:
data (dict): Original dict to mangle
+ regex, replacement (str): arguments to re.sub(regex, replacement, ...)
+ abs_path (list): if data is a config dict and no_tag_node_value_mangle is True
+ then abs_path should be the absolute config path to the first
+ keys of data, non-inclusive
+ no_tag_node_value_mangle (bool): do not mangle keys of tag node values
Returns: dict
"""
- from vyos.xml import is_tag
-
- new_dict = {}
+ import re
+ from vyos.xml_ref import is_tag_value
- for key in data.keys():
- save_mod = mod
- save_path = abs_path[:]
+ if abs_path is None:
+ abs_path = []
- abs_path.append(key)
+ new_dict = type(data)()
- if not is_tag(abs_path):
- new_key = re.sub(regex, replacement, key)
+ for k in data.keys():
+ if no_tag_node_value_mangle and is_tag_value(abs_path + [k]):
+ new_key = k
else:
- if mod%2:
- new_key = key
- else:
- new_key = re.sub(regex, replacement, key)
- if no_tag_node_value_mangle:
- mod += 1
+ new_key = re.sub(regex, replacement, k)
- value = data[key]
+ value = data[k]
if isinstance(value, dict):
- new_dict[new_key] = _mangle_dict_keys(value, regex, replacement, abs_path=abs_path, mod=mod, no_tag_node_value_mangle=no_tag_node_value_mangle)
+ new_dict[new_key] = mangle_dict_keys(value, regex, replacement,
+ abs_path=abs_path + [k],
+ no_tag_node_value_mangle=no_tag_node_value_mangle)
else:
new_dict[new_key] = value
- mod = save_mod
- abs_path = save_path[:]
-
return new_dict
-def mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_mangle=False):
- return _mangle_dict_keys(data, regex, replacement, abs_path=abs_path, no_tag_node_value_mangle=no_tag_node_value_mangle, mod=0)
-
def _get_sub_dict(d, lpath):
k = lpath[0]
if k not in d.keys():
@@ -234,6 +228,27 @@ def dict_to_list(d, save_key_to=None):
return collect
+def dict_to_paths(d: dict) -> list:
+ """ Generator to return list of paths from dict of list[str]|str
+ """
+ def func(d, path):
+ if isinstance(d, dict):
+ if not d:
+ yield path
+ for k, v in d.items():
+ for r in func(v, path + [k]):
+ yield r
+ elif isinstance(d, list):
+ for i in d:
+ for r in func(i, path):
+ yield r
+ elif isinstance(d, str):
+ yield path + [d]
+ else:
+ raise ValueError('object is not a dict of strings/list of strings')
+ for r in func(d, []):
+ yield r
+
def check_mutually_exclusive_options(d, keys, required=False):
""" Checks if a dict has at most one or only one of
mutually exclusive keys.
@@ -254,3 +269,39 @@ def check_mutually_exclusive_options(d, keys, required=False):
if required and (len(present_keys) < 1):
raise ValueError(f"At least one of the following options is required: {orig_keys}")
+
+class FixedDict(dict):
+ """
+ FixedDict: A dictionnary not allowing new keys to be created after initialisation.
+
+ >>> f = FixedDict(**{'count':1})
+ >>> f['count'] = 2
+ >>> f['king'] = 3
+ File "...", line ..., in __setitem__
+ raise ConfigError(f'Option "{k}" has no defined default')
+ """
+
+ from vyos import ConfigError
+
+ def __init__(self, **options):
+ self._allowed = options.keys()
+ super().__init__(**options)
+
+ def __setitem__(self, k, v):
+ """
+ __setitem__ is a builtin which is called by python when setting dict values:
+ >>> d = dict()
+ >>> d['key'] = 'value'
+ >>> d
+ {'key': 'value'}
+
+ is syntaxic sugar for
+
+ >>> d = dict()
+ >>> d.__setitem__('key','value')
+ >>> d
+ {'key': 'value'}
+ """
+ if k not in self._allowed:
+ raise ConfigError(f'Option "{k}" has no defined default')
+ super().__setitem__(k, v)
diff --git a/python/vyos/utils/file.py b/python/vyos/utils/file.py
index 2560a35be..667a2464b 100644
--- a/python/vyos/utils/file.py
+++ b/python/vyos/utils/file.py
@@ -14,7 +14,19 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
+from vyos.utils.permission import chown
+def makedir(path, user=None, group=None):
+ if os.path.exists(path):
+ return
+ os.makedirs(path, mode=0o755)
+ chown(path, user, group)
+
+def file_is_persistent(path):
+ import re
+ location = r'^(/config|/opt/vyatta/etc/config)'
+ absolute = os.path.abspath(os.path.dirname(path))
+ return re.match(location,absolute)
def read_file(fname, defaultonfailure=None):
"""
diff --git a/python/vyos/utils/kernel.py b/python/vyos/utils/kernel.py
new file mode 100644
index 000000000..1f3bbdffe
--- /dev/null
+++ b/python/vyos/utils/kernel.py
@@ -0,0 +1,38 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+def check_kmod(k_mod):
+ """ Common utility function to load required kernel modules on demand """
+ from vyos import ConfigError
+ from vyos.utils.process import call
+ if isinstance(k_mod, str):
+ k_mod = k_mod.split()
+ for module in k_mod:
+ if not os.path.exists(f'/sys/module/{module}'):
+ if call(f'modprobe {module}') != 0:
+ raise ConfigError(f'Loading Kernel module {module} failed')
+
+def unload_kmod(k_mod):
+ """ Common utility function to unload required kernel modules on demand """
+ from vyos import ConfigError
+ from vyos.utils.process import call
+ if isinstance(k_mod, str):
+ k_mod = k_mod.split()
+ for module in k_mod:
+ if os.path.exists(f'/sys/module/{module}'):
+ if call(f'rmmod {module}') != 0:
+ raise ConfigError(f'Unloading Kernel module {module} failed')
diff --git a/python/vyos/utils/list.py b/python/vyos/utils/list.py
new file mode 100644
index 000000000..63ef720ab
--- /dev/null
+++ b/python/vyos/utils/list.py
@@ -0,0 +1,20 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+def is_list_equal(first: list, second: list) -> bool:
+ """ Check if 2 lists are equal and list not empty """
+ if len(first) != len(second) or len(first) == 0:
+ return False
+ return sorted(first) == sorted(second)
diff --git a/python/vyos/utils/misc.py b/python/vyos/utils/misc.py
new file mode 100644
index 000000000..d82655914
--- /dev/null
+++ b/python/vyos/utils/misc.py
@@ -0,0 +1,66 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+def begin(*args):
+ """
+ Evaluate arguments in order and return the result of the *last* argument.
+ For combining multiple expressions in one statement. Useful for lambdas.
+ """
+ return args[-1]
+
+def begin0(*args):
+ """
+ Evaluate arguments in order and return the result of the *first* argument.
+ For combining multiple expressions in one statement. Useful for lambdas.
+ """
+ return args[0]
+
+def install_into_config(conf, config_paths, override_prompt=True):
+ # Allows op-mode scripts to install values if called from an active config session
+ # config_paths: dict of config paths
+ # override_prompt: if True, user will be prompted before existing nodes are overwritten
+ if not config_paths:
+ return None
+
+ from vyos.config import Config
+ from vyos.utils.io import ask_yes_no
+ from vyos.utils.process import cmd
+ if not Config().in_session():
+ print('You are not in configure mode, commands to install manually from configure mode:')
+ for path in config_paths:
+ print(f'set {path}')
+ 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
+
+ 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.')
diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py
new file mode 100644
index 000000000..2f181d8d9
--- /dev/null
+++ b/python/vyos/utils/network.py
@@ -0,0 +1,417 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+def _are_same_ip(one, two):
+ from socket import AF_INET
+ from socket import AF_INET6
+ from socket import inet_pton
+ from vyos.template import is_ipv4
+ # compare the binary representation of the IP
+ f_one = AF_INET if is_ipv4(one) else AF_INET6
+ s_two = AF_INET if is_ipv4(two) else AF_INET6
+ return inet_pton(f_one, one) == inet_pton(f_one, two)
+
+def get_protocol_by_name(protocol_name):
+ """Get protocol number by protocol name
+
+ % get_protocol_by_name('tcp')
+ % 6
+ """
+ import socket
+ try:
+ protocol_number = socket.getprotobyname(protocol_name)
+ return protocol_number
+ except socket.error:
+ return protocol_name
+
+def interface_exists(interface) -> bool:
+ import os
+ return os.path.exists(f'/sys/class/net/{interface}')
+
+def interface_exists_in_netns(interface_name, netns):
+ from vyos.utils.process import rc_cmd
+ rc, out = rc_cmd(f'ip netns exec {netns} ip link show dev {interface_name}')
+ if rc == 0:
+ return True
+ return False
+
+def get_vrf_members(vrf: str) -> list:
+ """
+ Get list of interface VRF members
+ :param vrf: str
+ :return: list
+ """
+ import json
+ from vyos.utils.process import cmd
+ if not interface_exists(vrf):
+ raise ValueError(f'VRF "{vrf}" does not exist!')
+ output = cmd(f'ip --json --brief link show master {vrf}')
+ answer = json.loads(output)
+ interfaces = []
+ for data in answer:
+ if 'ifname' in data:
+ interfaces.append(data.get('ifname'))
+ return interfaces
+
+def get_interface_vrf(interface):
+ """ Returns VRF of given interface """
+ from vyos.utils.dict import dict_search
+ from vyos.utils.network import get_interface_config
+ tmp = get_interface_config(interface)
+ if dict_search('linkinfo.info_slave_kind', tmp) == 'vrf':
+ return tmp['master']
+ return 'default'
+
+def get_interface_config(interface):
+ """ Returns the used encapsulation protocol for given interface.
+ If interface does not exist, None is returned.
+ """
+ import os
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ return None
+ from json import loads
+ from vyos.utils.process import cmd
+ tmp = loads(cmd(f'ip --detail --json link show dev {interface}'))[0]
+ return tmp
+
+def get_interface_address(interface):
+ """ Returns the used encapsulation protocol for given interface.
+ If interface does not exist, None is returned.
+ """
+ import os
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ return None
+ from json import loads
+ from vyos.utils.process import cmd
+ tmp = loads(cmd(f'ip --detail --json addr show dev {interface}'))[0]
+ return tmp
+
+def get_interface_namespace(iface):
+ """
+ Returns wich netns the interface belongs to
+ """
+ from json import loads
+ from vyos.utils.process import cmd
+ # Check if netns exist
+ tmp = loads(cmd(f'ip --json netns ls'))
+ if len(tmp) == 0:
+ return None
+
+ for ns in tmp:
+ netns = f'{ns["name"]}'
+ # Search interface in each netns
+ data = loads(cmd(f'ip netns exec {netns} ip --json link show'))
+ for tmp in data:
+ if iface == tmp["ifname"]:
+ return netns
+
+def is_wwan_connected(interface):
+ """ Determine if a given WWAN interface, e.g. wwan0 is connected to the
+ carrier network or not """
+ import json
+ from vyos.utils.process import cmd
+
+ 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')
+ tmp = json.loads(tmp)
+
+ # return True/False if interface is in connected state
+ return dict_search('modem.generic.state', tmp) == 'connected'
+
+def get_bridge_fdb(interface):
+ """ Returns the forwarding database entries for a given interface """
+ import os
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ return None
+ from json import loads
+ from vyos.utils.process import cmd
+ tmp = loads(cmd(f'bridge -j fdb show dev {interface}'))
+ return tmp
+
+def get_all_vrfs():
+ """ Return a dictionary of all system wide known VRF instances """
+ from json import loads
+ from vyos.utils.process import cmd
+ tmp = loads(cmd('ip --json vrf list'))
+ # Result is of type [{"name":"red","table":1000},{"name":"blue","table":2000}]
+ # so we will re-arrange it to a more nicer representation:
+ # {'red': {'table': 1000}, 'blue': {'table': 2000}}
+ data = {}
+ for entry in tmp:
+ name = entry.pop('name')
+ data[name] = entry
+ return data
+
+def mac2eui64(mac, prefix=None):
+ """
+ Convert a MAC address to a EUI64 address or, with prefix provided, a full
+ IPv6 address.
+ Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3
+ """
+ import re
+ from ipaddress import ip_network
+ # http://tools.ietf.org/html/rfc4291#section-2.5.1
+ eui64 = re.sub(r'[.:-]', '', mac).lower()
+ eui64 = eui64[0:6] + 'fffe' + eui64[6:]
+ eui64 = hex(int(eui64[0:2], 16) ^ 2)[2:].zfill(2) + eui64[2:]
+
+ if prefix is None:
+ return ':'.join(re.findall(r'.{4}', eui64))
+ else:
+ try:
+ net = ip_network(prefix, strict=False)
+ euil = int('0x{0}'.format(eui64), 16)
+ return str(net[euil])
+ except: # pylint: disable=bare-except
+ return
+
+def check_port_availability(ipaddress, port, protocol):
+ """
+ Check if port is available and not used by any service
+ Return False if a port is busy or IP address does not exists
+ Should be used carefully for services that can start listening
+ dynamically, because IP address may be dynamic too
+ """
+ from socketserver import TCPServer, UDPServer
+ from ipaddress import ip_address
+
+ # verify arguments
+ try:
+ ipaddress = ip_address(ipaddress).compressed
+ except:
+ raise ValueError(f'The {ipaddress} is not a valid IPv4 or IPv6 address')
+ if port not in range(1, 65536):
+ raise ValueError(f'The port number {port} is not in the 1-65535 range')
+ if protocol not in ['tcp', 'udp']:
+ raise ValueError(f'The protocol {protocol} is not supported. Only tcp and udp are allowed')
+
+ # check port availability
+ try:
+ if protocol == 'tcp':
+ server = TCPServer((ipaddress, port), None, bind_and_activate=True)
+ if protocol == 'udp':
+ server = UDPServer((ipaddress, port), None, bind_and_activate=True)
+ server.server_close()
+ except Exception as e:
+ # errno.h:
+ #define EADDRINUSE 98 /* Address already in use */
+ if e.errno == 98:
+ return False
+
+ return True
+
+def is_listen_port_bind_service(port: int, service: str) -> bool:
+ """Check if listen port bound to expected program name
+ :param port: Bind port
+ :param service: Program name
+ :return: bool
+
+ Example:
+ % is_listen_port_bind_service(443, 'nginx')
+ True
+ % is_listen_port_bind_service(443, 'ocserv-main')
+ False
+ """
+ from psutil import net_connections as connections
+ from psutil import Process as process
+ for connection in connections():
+ addr = connection.laddr
+ pid = connection.pid
+ pid_name = process(pid).name()
+ pid_port = addr.port
+ if service == pid_name and port == pid_port:
+ return True
+ return False
+
+def is_ipv6_link_local(addr):
+ """ Check if addrsss is an IPv6 link-local address. Returns True/False """
+ from ipaddress import ip_interface
+ from vyos.template import is_ipv6
+ addr = addr.split('%')[0]
+ if is_ipv6(addr):
+ if ip_interface(addr).is_link_local:
+ return True
+
+ return False
+
+def is_addr_assigned(ip_address, vrf=None) -> bool:
+ """ Verify if the given IPv4/IPv6 address is assigned to any interface """
+ from netifaces import interfaces
+ from vyos.utils.network import get_interface_config
+ from vyos.utils.dict 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
+
+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
+
+ # check if the requested address type is configured at all
+ # {
+ # 17: [{'addr': '08:00:27:d9:5b:04', 'broadcast': 'ff:ff:ff:ff:ff:ff'}],
+ # 2: [{'addr': '10.0.2.15', 'netmask': '255.255.255.0', 'broadcast': '10.0.2.255'}],
+ # 10: [{'addr': 'fe80::a00:27ff:fed9:5b04%eth0', 'netmask': 'ffff:ffff:ffff:ffff::'}]
+ # }
+ try:
+ addresses = ifaddresses(intf)
+ except ValueError as e:
+ print(e)
+ return False
+
+ # determine IP version (AF_INET or AF_INET6) depending on passed address
+ 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 addresses.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::'}
+ ip_addr = ip['addr'].split('%')[0]
+
+ if not _are_same_ip(address, ip_addr):
+ continue
+
+ # we do not have a netmask to compare against, they are the same
+ if not netmask:
+ return True
+
+ prefixlen = ''
+ if is_ipv4(ip_addr):
+ prefixlen = sum([bin(int(_)).count('1') for _ in ip['netmask'].split('.')])
+ else:
+ prefixlen = sum([bin(int(_,16)).count('1') for _ in ip['netmask'].split('/')[0].split(':') if _])
+
+ if str(prefixlen) == netmask:
+ return True
+
+ return False
+
+def is_loopback_addr(addr):
+ """ Check if supplied IPv4/IPv6 address is a loopback address """
+ from ipaddress import ip_address
+ return ip_address(addr).is_loopback
+
+def is_wireguard_key_pair(private_key: str, public_key:str) -> bool:
+ """
+ Checks if public/private keys are keypair
+ :param private_key: Wireguard private key
+ :type private_key: str
+ :param public_key: Wireguard public key
+ :type public_key: str
+ :return: If public/private keys are keypair returns True else False
+ :rtype: bool
+ """
+ from vyos.utils.process import cmd
+ gen_public_key = cmd('wg pubkey', input=private_key)
+ if gen_public_key == public_key:
+ return True
+ else:
+ return False
+
+def is_subnet_connected(subnet, primary=False):
+ """
+ Verify is the given IPv4/IPv6 subnet is connected to any interface on this
+ system.
+
+ primary check if the subnet is reachable via the primary IP address of this
+ interface, or in other words has a broadcast address configured. ISC DHCP
+ for instance will complain if it should listen on non broadcast interfaces.
+
+ Return True/False
+ """
+ from ipaddress import ip_address
+ from ipaddress import ip_network
+
+ from netifaces import ifaddresses
+ from netifaces import interfaces
+ from netifaces import AF_INET
+ from netifaces import AF_INET6
+
+ from vyos.template import is_ipv6
+
+ # determine IP version (AF_INET or AF_INET6) depending on passed address
+ addr_type = AF_INET
+ if is_ipv6(subnet):
+ addr_type = AF_INET6
+
+ for interface in interfaces():
+ # check if the requested address type is configured at all
+ if addr_type not in ifaddresses(interface).keys():
+ continue
+
+ # An interface can have multiple addresses, but some software components
+ # only support the primary address :(
+ if primary:
+ ip = ifaddresses(interface)[addr_type][0]['addr']
+ if ip_address(ip) in ip_network(subnet):
+ return True
+ else:
+ # Check every assigned IP address if it is connected to the subnet
+ # in question
+ for ip in ifaddresses(interface)[addr_type]:
+ # remove interface extension (e.g. %eth0) that gets thrown on the end of _some_ addrs
+ addr = ip['addr'].split('%')[0]
+ if ip_address(addr) in ip_network(subnet):
+ return True
+
+ return False
+
+def is_afi_configured(interface, afi):
+ """ Check if given address family is configured, or in other words - an IP
+ address is assigned to the interface. """
+ from netifaces import ifaddresses
+ from netifaces import AF_INET
+ from netifaces import AF_INET6
+
+ if afi not in [AF_INET, AF_INET6]:
+ raise ValueError('Address family must be in [AF_INET, AF_INET6]')
+
+ try:
+ addresses = ifaddresses(interface)
+ except ValueError as e:
+ print(e)
+ return False
+
+ return afi in addresses
diff --git a/python/vyos/utils/permission.py b/python/vyos/utils/permission.py
new file mode 100644
index 000000000..d938b494f
--- /dev/null
+++ b/python/vyos/utils/permission.py
@@ -0,0 +1,78 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+def chown(path, user, group):
+ """ change file/directory owner """
+ from pwd import getpwnam
+ from grp import getgrnam
+
+ if user is None or group is None:
+ return False
+
+ # path may also be an open file descriptor
+ if not isinstance(path, int) and not os.path.exists(path):
+ return False
+
+ uid = getpwnam(user).pw_uid
+ gid = getgrnam(group).gr_gid
+ os.chown(path, uid, gid)
+ return True
+
+def chmod(path, bitmask):
+ # path may also be an open file descriptor
+ if not isinstance(path, int) and not os.path.exists(path):
+ return
+ if bitmask is None:
+ return
+ os.chmod(path, bitmask)
+
+def chmod_600(path):
+ """ make file only read/writable by owner """
+ from stat import S_IRUSR, S_IWUSR
+
+ bitmask = S_IRUSR | S_IWUSR
+ chmod(path, bitmask)
+
+def chmod_750(path):
+ """ make file/directory only executable to user and group """
+ from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP
+
+ bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
+ chmod(path, bitmask)
+
+def chmod_755(path):
+ """ make file executable by all """
+ from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
+
+ bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | \
+ S_IROTH | S_IXOTH
+ chmod(path, bitmask)
+
+def is_admin() -> bool:
+ """Look if current user is in sudo group"""
+ from getpass import getuser
+ from grp import getgrnam
+ current_user = getuser()
+ (_, _, _, admin_group_members) = getgrnam('sudo')
+ return current_user in admin_group_members
+
+def get_cfg_group_id():
+ from grp import getgrnam
+ from vyos.defaults import cfg_group
+
+ group_data = getgrnam(cfg_group)
+ return group_data.gr_gid
diff --git a/python/vyos/utils/process.py b/python/vyos/utils/process.py
new file mode 100644
index 000000000..e09c7d86d
--- /dev/null
+++ b/python/vyos/utils/process.py
@@ -0,0 +1,232 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from subprocess import Popen
+from subprocess import PIPE
+from subprocess import STDOUT
+from subprocess import DEVNULL
+
+def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=PIPE, stderr=PIPE, decode='utf-8'):
+ """
+ popen is a wrapper helper aound subprocess.Popen
+ with it default setting it will return a tuple (out, err)
+ out: the output of the program run
+ err: the error code returned by the program
+
+ it can be affected by the following flags:
+ shell: do not try to auto-detect if a shell is required
+ for example if a pipe (|) or redirection (>, >>) is used
+ input: data to sent to the child process via STDIN
+ the data should be bytes but string will be converted
+ timeout: time after which the command will be considered to have failed
+ env: mapping that defines the environment variables for the new process
+ stdout: define how the output of the program should be handled
+ - PIPE (default), sends stdout to the output
+ - DEVNULL, discard the output
+ stderr: define how the output of the program should be handled
+ - None (default), send/merge the data to/with stderr
+ - PIPE, popen will append it to output
+ - STDOUT, send the data to be merged with stdout
+ - DEVNULL, discard the output
+ decode: specify the expected text encoding (utf-8, ascii, ...)
+ the default is explicitely utf-8 which is python's own default
+
+ usage:
+ get both stdout and stderr: popen('command', stdout=PIPE, stderr=STDOUT)
+ discard stdout and get stderr: popen('command', stdout=DEVNUL, stderr=PIPE)
+ """
+
+ # airbag must be left as an import in the function as otherwise we have a
+ # a circual import dependency
+ from vyos import debug
+ from vyos import airbag
+
+ # log if the flag is set, otherwise log if command is set
+ if not debug.enabled(flag):
+ flag = 'command'
+
+ cmd_msg = f"cmd '{command}'"
+ debug.message(cmd_msg, flag)
+
+ use_shell = shell
+ stdin = None
+ if shell is None:
+ use_shell = False
+ if ' ' in command:
+ use_shell = True
+ if env:
+ use_shell = True
+
+ if input:
+ stdin = PIPE
+ input = input.encode() if type(input) is str else input
+
+ p = Popen(command, stdin=stdin, stdout=stdout, stderr=stderr,
+ env=env, shell=use_shell)
+
+ pipe = p.communicate(input, timeout)
+
+ pipe_out = b''
+ if stdout == PIPE:
+ pipe_out = pipe[0]
+
+ pipe_err = b''
+ if stderr == PIPE:
+ pipe_err = pipe[1]
+
+ str_out = pipe_out.decode(decode).replace('\r\n', '\n').strip()
+ str_err = pipe_err.decode(decode).replace('\r\n', '\n').strip()
+
+ out_msg = f"returned (out):\n{str_out}"
+ if str_out:
+ debug.message(out_msg, flag)
+
+ if str_err:
+ from sys import stderr
+ err_msg = f"returned (err):\n{str_err}"
+ # this message will also be send to syslog via airbag
+ debug.message(err_msg, flag, destination=stderr)
+
+ # should something go wrong, report this too via airbag
+ airbag.noteworthy(cmd_msg)
+ airbag.noteworthy(out_msg)
+ airbag.noteworthy(err_msg)
+
+ return str_out, p.returncode
+
+
+def run(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=DEVNULL, stderr=PIPE, decode='utf-8'):
+ """
+ A wrapper around popen, which discard the stdout and
+ will return the error code of a command
+ """
+ _, code = popen(
+ command, flag,
+ stdout=stdout, stderr=stderr,
+ input=input, timeout=timeout,
+ env=env, shell=shell,
+ decode=decode,
+ )
+ return code
+
+
+def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=PIPE, stderr=PIPE, decode='utf-8', raising=None, message='',
+ expect=[0]):
+ """
+ A wrapper around popen, which returns the stdout and
+ will raise the error code of a command
+
+ raising: specify which call should be used when raising
+ the class should only require a string as parameter
+ (default is OSError) with the error code
+ expect: a list of error codes to consider as normal
+ """
+ decoded, code = popen(
+ command, flag,
+ stdout=stdout, stderr=stderr,
+ input=input, timeout=timeout,
+ env=env, shell=shell,
+ decode=decode,
+ )
+ if code not in expect:
+ feedback = message + '\n' if message else ''
+ feedback += f'failed to run command: {command}\n'
+ feedback += f'returned: {decoded}\n'
+ feedback += f'exit code: {code}'
+ if raising is None:
+ # error code can be recovered with .errno
+ raise OSError(code, feedback)
+ else:
+ raise raising(feedback)
+ return decoded
+
+
+def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=PIPE, stderr=STDOUT, decode='utf-8'):
+ """
+ A wrapper around popen, which returns the return code
+ of a command and stdout
+
+ % rc_cmd('uname')
+ (0, 'Linux')
+ % rc_cmd('ip link show dev eth99')
+ (1, 'Device "eth99" does not exist.')
+ """
+ out, code = popen(
+ command, flag,
+ stdout=stdout, stderr=stderr,
+ input=input, timeout=timeout,
+ env=env, shell=shell,
+ decode=decode,
+ )
+ return code, out
+
+def call(command, flag='', shell=None, input=None, timeout=None, env=None,
+ stdout=None, stderr=None, decode='utf-8'):
+ """
+ A wrapper around popen, which print the stdout and
+ will return the error code of a command
+ """
+ out, code = popen(
+ command, flag,
+ stdout=stdout, stderr=stderr,
+ input=input, timeout=timeout,
+ env=env, shell=shell,
+ decode=decode,
+ )
+ if out:
+ print(out)
+ return code
+
+def process_running(pid_file):
+ """ Checks if a process with PID in pid_file is running """
+ from psutil import pid_exists
+ if not os.path.isfile(pid_file):
+ return False
+ with open(pid_file, 'r') as f:
+ pid = f.read().strip()
+ return pid_exists(int(pid))
+
+def process_named_running(name, cmdline: str=None):
+ """ Checks if process with given name is running and returns its PID.
+ If Process is not running, return None
+ """
+ from psutil import process_iter
+ for p in process_iter(['name', 'pid', 'cmdline']):
+ if cmdline:
+ if p.info['name'] == name and cmdline in p.info['cmdline']:
+ return p.info['pid']
+ elif p.info['name'] == name:
+ return p.info['pid']
+ return None
+
+def is_systemd_service_active(service):
+ """ Test is a specified systemd service is activated.
+ Returns True if service is active, false otherwise.
+ Copied from: https://unix.stackexchange.com/a/435317 """
+ tmp = cmd(f'systemctl show --value -p ActiveState {service}')
+ return bool((tmp == 'active'))
+
+def is_systemd_service_running(service):
+ """ Test is a specified systemd service is actually running.
+ Returns True if service is running, false otherwise.
+ Copied from: https://unix.stackexchange.com/a/435317 """
+ tmp = cmd(f'systemctl show --value -p SubState {service}')
+ return bool((tmp == 'running'))
diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py
new file mode 100644
index 000000000..5d41c0c05
--- /dev/null
+++ b/python/vyos/utils/system.py
@@ -0,0 +1,107 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+from subprocess import run
+
+def sysctl_read(name: str) -> str:
+ """Read and return current value of sysctl() option
+
+ Args:
+ name (str): sysctl key name
+
+ Returns:
+ str: sysctl key value
+ """
+ tmp = run(['sysctl', '-nb', name], capture_output=True)
+ return tmp.stdout.decode()
+
+def sysctl_write(name: str, value: str | int) -> bool:
+ """Change value via sysctl()
+
+ Args:
+ name (str): sysctl key name
+ value (str | int): sysctl key value
+
+ Returns:
+ bool: True if changed, False otherwise
+ """
+ # convert other types to string before comparison
+ if not isinstance(value, str):
+ value = str(value)
+ # do not change anything if a value is already configured
+ if sysctl_read(name) == value:
+ return True
+ # return False if sysctl call failed
+ if run(['sysctl', '-wq', f'{name}={value}']).returncode != 0:
+ return False
+ # compare old and new values
+ # sysctl may apply value, but its actual value will be
+ # different from requested
+ if sysctl_read(name) == value:
+ return True
+ # False in other cases
+ return False
+
+def sysctl_apply(sysctl_dict: dict[str, str], revert: bool = True) -> bool:
+ """Apply sysctl values.
+
+ Args:
+ sysctl_dict (dict[str, str]): dictionary with sysctl keys with values
+ revert (bool, optional): Revert to original values if new were not
+ applied. Defaults to True.
+
+ Returns:
+ bool: True if all params configured properly, False in other cases
+ """
+ # get current values
+ sysctl_original: dict[str, str] = {}
+ for key_name in sysctl_dict.keys():
+ sysctl_original[key_name] = sysctl_read(key_name)
+ # apply new values and revert in case one of them was not applied
+ for key_name, value in sysctl_dict.items():
+ if not sysctl_write(key_name, value):
+ if revert:
+ sysctl_apply(sysctl_original, revert=False)
+ return False
+ # everything applied
+ return True
+
+def get_half_cpus():
+ """ return 1/2 of the numbers of available CPUs """
+ cpu = os.cpu_count()
+ if cpu > 1:
+ cpu /= 2
+ return int(cpu)
+
+def find_device_file(device):
+ """ Recurively search /dev for the given device file and return its full path.
+ If no device file was found 'None' is returned """
+ from fnmatch import fnmatch
+
+ for root, dirs, files in os.walk('/dev'):
+ for basename in files:
+ if fnmatch(basename, device):
+ return os.path.join(root, basename)
+
+ return None
+
+def load_as_module(name: str, path: str):
+ import importlib.util
+
+ spec = importlib.util.spec_from_file_location(name, path)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
+ return mod
diff --git a/python/vyos/validate.py b/python/vyos/validate.py
deleted file mode 100644
index d3e6e9087..000000000
--- a/python/vyos/validate.py
+++ /dev/null
@@ -1,259 +0,0 @@
-# Copyright 2018-2021 VyOS maintainers and contributors <maintainers@vyos.io>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-# Important note when you are adding new validation functions:
-#
-# The Control class will analyse the signature of the function in this file
-# and will build the parameters to be passed to it.
-#
-# The parameter names "ifname" and "self" will get the Interface name and class
-# parameters with default will be left unset
-# all other paramters will receive the value to check
-
-def is_ipv6_link_local(addr):
- """ Check if addrsss is an IPv6 link-local address. Returns True/False """
- from ipaddress import ip_interface
- from vyos.template import is_ipv6
- addr = addr.split('%')[0]
- if is_ipv6(addr):
- if ip_interface(addr).is_link_local:
- return True
-
- return False
-
-def _are_same_ip(one, two):
- from socket import AF_INET
- from socket import AF_INET6
- from socket import inet_pton
- from vyos.template import is_ipv4
- # compare the binary representation of the IP
- f_one = AF_INET if is_ipv4(one) else AF_INET6
- s_two = AF_INET if is_ipv4(two) else AF_INET6
- return inet_pton(f_one, one) == inet_pton(f_one, two)
-
-def is_intf_addr_assigned(ifname, addr, netns=None):
- """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.
- """
- import json
- import jmespath
- from vyos.util import rc_cmd
- from ipaddress import ip_interface
-
- within_netns = f'ip netns exec {netns}' if netns else ''
- rc, out = rc_cmd(f'{within_netns} ip --json address show dev {ifname}')
- if rc == 0:
- json_out = json.loads(out)
- addresses = jmespath.search("[].addr_info[].{family: family, address: local, prefixlen: prefixlen}", json_out)
- for address_info in addresses:
- family = address_info['family']
- address = address_info['address']
- prefixlen = address_info['prefixlen']
- # Remove the interface name if present in the given address
- if '%' in addr:
- addr = addr.split('%')[0]
- interface = ip_interface(f"{address}/{prefixlen}")
- if ip_interface(addr) == interface or address == addr:
- return True
-
- return False
-
-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
- 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
-
-def is_loopback_addr(addr):
- """ Check if supplied IPv4/IPv6 address is a loopback address """
- from ipaddress import ip_address
- return ip_address(addr).is_loopback
-
-def is_subnet_connected(subnet, primary=False):
- """
- Verify is the given IPv4/IPv6 subnet is connected to any interface on this
- system.
-
- primary check if the subnet is reachable via the primary IP address of this
- interface, or in other words has a broadcast address configured. ISC DHCP
- for instance will complain if it should listen on non broadcast interfaces.
-
- Return True/False
- """
- from ipaddress import ip_address
- from ipaddress import ip_network
-
- from netifaces import ifaddresses
- from netifaces import interfaces
- from netifaces import AF_INET
- from netifaces import AF_INET6
-
- from vyos.template import is_ipv6
-
- # determine IP version (AF_INET or AF_INET6) depending on passed address
- addr_type = AF_INET
- if is_ipv6(subnet):
- addr_type = AF_INET6
-
- for interface in interfaces():
- # check if the requested address type is configured at all
- if addr_type not in ifaddresses(interface).keys():
- continue
-
- # An interface can have multiple addresses, but some software components
- # only support the primary address :(
- if primary:
- ip = ifaddresses(interface)[addr_type][0]['addr']
- if ip_address(ip) in ip_network(subnet):
- return True
- else:
- # Check every assigned IP address if it is connected to the subnet
- # in question
- for ip in ifaddresses(interface)[addr_type]:
- # remove interface extension (e.g. %eth0) that gets thrown on the end of _some_ addrs
- addr = ip['addr'].split('%')[0]
- if ip_address(addr) in ip_network(subnet):
- return True
-
- return False
-
-
-def assert_boolean(b):
- if int(b) not in (0, 1):
- raise ValueError(f'Value {b} out of range')
-
-
-def assert_range(value, lower=0, count=3):
- if int(value, 16) not in range(lower, lower+count):
- raise ValueError("Value out of range")
-
-
-def assert_list(s, l):
- if s not in l:
- o = ' or '.join([f'"{n}"' for n in l])
- raise ValueError(f'state must be {o}, got {s}')
-
-
-def assert_number(n):
- if not str(n).isnumeric():
- raise ValueError(f'{n} must be a number')
-
-
-def assert_positive(n, smaller=0):
- assert_number(n)
- if int(n) < smaller:
- raise ValueError(f'{n} is smaller than {smaller}')
-
-
-def assert_mtu(mtu, ifname):
- assert_number(mtu)
-
- import json
- from vyos.util import cmd
- out = cmd(f'ip -j -d link show dev {ifname}')
- # [{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","linkmode":"DEFAULT","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:d9:5b:04","broadcast":"ff:ff:ff:ff:ff:ff","promiscuity":0,"min_mtu":46,"max_mtu":16110,"inet6_addr_gen_mode":"none","num_tx_queues":1,"num_rx_queues":1,"gso_max_size":65536,"gso_max_segs":65535}]
- parsed = json.loads(out)[0]
- min_mtu = int(parsed.get('min_mtu', '0'))
- # cur_mtu = parsed.get('mtu',0),
- max_mtu = int(parsed.get('max_mtu', '0'))
- cur_mtu = int(mtu)
-
- if (min_mtu and cur_mtu < min_mtu) or cur_mtu < 68:
- raise ValueError(f'MTU is too small for interface "{ifname}": {mtu} < {min_mtu}')
- if (max_mtu and cur_mtu > max_mtu) or cur_mtu > 65536:
- raise ValueError(f'MTU is too small for interface "{ifname}": {mtu} > {max_mtu}')
-
-
-def assert_mac(m):
- split = m.split(':')
- size = len(split)
-
- # a mac address consits out of 6 octets
- if size != 6:
- raise ValueError(f'wrong number of MAC octets ({size}): {m}')
-
- octets = []
- try:
- for octet in split:
- octets.append(int(octet, 16))
- except ValueError:
- raise ValueError(f'invalid hex number "{octet}" in : {m}')
-
- # validate against the first mac address byte if it's a multicast
- # address
- if octets[0] & 1:
- raise ValueError(f'{m} is a multicast MAC address')
-
- # overall mac address is not allowed to be 00:00:00:00:00:00
- if sum(octets) == 0:
- raise ValueError('00:00:00:00:00:00 is not a valid MAC address')
-
- if octets[:5] == (0, 0, 94, 0, 1):
- raise ValueError(f'{m} is a VRRP MAC address')
-
-def has_address_configured(conf, intf):
- """
- Checks if interface has an address configured.
- Checks the following config nodes:
- 'address', 'ipv6 address eui64', 'ipv6 address autoconf'
-
- Returns True if interface has address configured, False if it doesn't.
- """
- from vyos.ifconfig import Section
- ret = False
-
- old_level = conf.get_level()
- conf.set_level([])
-
- intfpath = 'interfaces ' + Section.get_config_path(intf)
- if ( conf.exists(f'{intfpath} address') or
- conf.exists(f'{intfpath} ipv6 address autoconf') or
- conf.exists(f'{intfpath} ipv6 address eui64') ):
- ret = True
-
- conf.set_level(old_level)
- return ret
-
-def has_vrf_configured(conf, intf):
- """
- Checks if interface has a VRF configured.
-
- Returns True if interface has VRF configured, False if it doesn't.
- """
- from vyos.ifconfig import Section
- ret = False
-
- old_level = conf.get_level()
- conf.set_level([])
-
- tmp = ['interfaces', Section.get_config_path(intf), 'vrf']
- if conf.exists(tmp):
- ret = True
-
- conf.set_level(old_level)
- return ret
diff --git a/python/vyos/version.py b/python/vyos/version.py
index fb706ad44..1c5651c83 100644
--- a/python/vyos/version.py
+++ b/python/vyos/version.py
@@ -1,4 +1,4 @@
-# Copyright 2017-2020 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2017-2023 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
@@ -34,11 +34,11 @@ import json
import requests
import vyos.defaults
-from vyos.util import read_file
-from vyos.util import read_json
-from vyos.util import popen
-from vyos.util import run
-from vyos.util import DEVNULL
+from vyos.utils.file import read_file
+from vyos.utils.file import read_json
+from vyos.utils.process import popen
+from vyos.utils.process import run
+from vyos.utils.process import DEVNULL
version_file = os.path.join(vyos.defaults.directories['data'], 'version.json')
diff --git a/python/vyos/vpp.py b/python/vyos/vpp.py
new file mode 100644
index 000000000..76e5d29c3
--- /dev/null
+++ b/python/vyos/vpp.py
@@ -0,0 +1,315 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from functools import wraps
+from pathlib import Path
+from re import search as re_search, fullmatch as re_fullmatch, MULTILINE as re_M
+from subprocess import run
+from time import sleep
+
+from vpp_papi import VPPApiClient
+from vpp_papi import VPPIOError, VPPValueError
+
+
+class VPPControl:
+ """Control VPP network stack
+ """
+
+ class _Decorators:
+ """Decorators for VPPControl
+ """
+
+ @classmethod
+ def api_call(cls, decorated_func):
+ """Check if API is connected before API call
+
+ Args:
+ decorated_func: function to decorate
+
+ Raises:
+ VPPIOError: Connection to API is not established
+ """
+
+ @wraps(decorated_func)
+ def api_safe_wrapper(cls, *args, **kwargs):
+ if not cls.vpp_api_client.transport.connected:
+ raise VPPIOError(2, 'VPP API is not connected')
+ return decorated_func(cls, *args, **kwargs)
+
+ return api_safe_wrapper
+
+ @classmethod
+ def check_retval(cls, decorated_func):
+ """Check retval from API response
+
+ Args:
+ decorated_func: function to decorate
+
+ Raises:
+ VPPValueError: raised when retval is not 0
+ """
+
+ @wraps(decorated_func)
+ def check_retval_wrapper(cls, *args, **kwargs):
+ return_value = decorated_func(cls, *args, **kwargs)
+ if not return_value.retval == 0:
+ raise VPPValueError(
+ f'VPP API call failed: {return_value.retval}')
+ return return_value
+
+ return check_retval_wrapper
+
+ def __init__(self, attempts: int = 5, interval: int = 1000) -> None:
+ """Create VPP API connection
+
+ Args:
+ attempts (int, optional): attempts to connect. Defaults to 5.
+ interval (int, optional): interval between attempts in ms. Defaults to 1000.
+
+ Raises:
+ VPPIOError: Connection to API cannot be established
+ """
+ self.vpp_api_client = VPPApiClient()
+ # connect with interval
+ while attempts:
+ try:
+ attempts -= 1
+ self.vpp_api_client.connect('vpp-vyos')
+ break
+ except (ConnectionRefusedError, FileNotFoundError) as err:
+ print(f'VPP API connection timeout: {err}')
+ sleep(interval / 1000)
+ # raise exception if connection was not successful in the end
+ if not self.vpp_api_client.transport.connected:
+ raise VPPIOError(2, 'Cannot connect to VPP API')
+
+ def __del__(self) -> None:
+ """Disconnect from VPP API (destructor)
+ """
+ self.disconnect()
+
+ def disconnect(self) -> None:
+ """Disconnect from VPP API
+ """
+ if self.vpp_api_client.transport.connected:
+ self.vpp_api_client.disconnect()
+
+ @_Decorators.check_retval
+ @_Decorators.api_call
+ def cli_cmd(self, command: str):
+ """Send raw CLI command
+
+ Args:
+ command (str): command to send
+
+ Returns:
+ vpp_papi.vpp_serializer.cli_inband_reply: CLI reply class
+ """
+ return self.vpp_api_client.api.cli_inband(cmd=command)
+
+ @_Decorators.api_call
+ def get_mac(self, ifname: str) -> str:
+ """Find MAC address by interface name in VPP
+
+ Args:
+ ifname (str): interface name inside VPP
+
+ Returns:
+ str: MAC address
+ """
+ for iface in self.vpp_api_client.api.sw_interface_dump():
+ if iface.interface_name == ifname:
+ return iface.l2_address.mac_string
+ return ''
+
+ @_Decorators.api_call
+ def get_sw_if_index(self, ifname: str) -> int | None:
+ """Find interface index by interface name in VPP
+
+ Args:
+ ifname (str): interface name inside VPP
+
+ Returns:
+ int | None: Interface index or None (if was not fount)
+ """
+ for iface in self.vpp_api_client.api.sw_interface_dump():
+ if iface.interface_name == ifname:
+ return iface.sw_if_index
+ return None
+
+ @_Decorators.check_retval
+ @_Decorators.api_call
+ def lcp_pair_add(self, iface_name_vpp: str, iface_name_kernel: str) -> None:
+ """Create LCP interface pair between VPP and kernel
+
+ Args:
+ iface_name_vpp (str): interface name in VPP
+ iface_name_kernel (str): interface name in kernel
+ """
+ iface_index = self.get_sw_if_index(iface_name_vpp)
+ if iface_index:
+ return self.vpp_api_client.api.lcp_itf_pair_add_del(
+ is_add=True,
+ sw_if_index=iface_index,
+ host_if_name=iface_name_kernel)
+
+ @_Decorators.check_retval
+ @_Decorators.api_call
+ def lcp_pair_del(self, iface_name_vpp: str, iface_name_kernel: str) -> None:
+ """Delete LCP interface pair between VPP and kernel
+
+ Args:
+ iface_name_vpp (str): interface name in VPP
+ iface_name_kernel (str): interface name in kernel
+ """
+ iface_index = self.get_sw_if_index(iface_name_vpp)
+ if iface_index:
+ return self.vpp_api_client.api.lcp_itf_pair_add_del(
+ is_add=False,
+ sw_if_index=iface_index,
+ host_if_name=iface_name_kernel)
+
+ @_Decorators.check_retval
+ @_Decorators.api_call
+ def iface_rxmode(self, iface_name: str, rx_mode: str) -> None:
+ """Set interface rx-mode in VPP
+
+ Args:
+ iface_name (str): interface name in VPP
+ rx_mode (str): mode (polling, interrupt, adaptive)
+ """
+ modes_dict: dict[str, int] = {
+ 'polling': 1,
+ 'interrupt': 2,
+ 'adaptive': 3
+ }
+ if rx_mode not in modes_dict:
+ raise VPPValueError(f'Mode {rx_mode} is not known')
+ iface_index = self.get_sw_if_index(iface_name)
+ return self.vpp_api_client.api.sw_interface_set_rx_mode(
+ sw_if_index=iface_index, mode=modes_dict[rx_mode])
+
+ @_Decorators.api_call
+ def get_pci_addr(self, ifname: str) -> str:
+ """Find PCI address of interface by interface name in VPP
+
+ Args:
+ ifname (str): interface name inside VPP
+
+ Returns:
+ str: PCI address
+ """
+ hw_info = self.cli_cmd(f'show hardware-interfaces {ifname}').reply
+
+ regex_filter = r'^\s+pci: device (?P<device>\w+:\w+) subsystem (?P<subsystem>\w+:\w+) address (?P<address>\w+:\w+:\w+\.\w+) numa (?P<numa>\w+)$'
+ re_obj = re_search(regex_filter, hw_info, re_M)
+
+ # return empty string if no interface or no PCI info was found
+ if not hw_info or not re_obj:
+ return ''
+
+ address = re_obj.groupdict().get('address', '')
+
+ # we need to modify address to math kernel style
+ # for example: 0000:06:14.00 -> 0000:06:14.0
+ address_chunks: list[str] = address.split('.')
+ address_normalized: str = f'{address_chunks[0]}.{int(address_chunks[1])}'
+
+ return address_normalized
+
+
+class HostControl:
+ """Control Linux host
+ """
+
+ @staticmethod
+ def pci_rescan(pci_addr: str = '') -> None:
+ """Rescan PCI device by removing it and rescan PCI bus
+
+ If PCI address is not defined - just rescan PCI bus
+
+ Args:
+ address (str, optional): PCI address of device. Defaults to ''.
+ """
+ if pci_addr:
+ device_file = Path(f'/sys/bus/pci/devices/{pci_addr}/remove')
+ if device_file.exists():
+ device_file.write_text('1')
+ # wait 10 seconds max until device will be removed
+ attempts = 100
+ while device_file.exists() and attempts:
+ attempts -= 1
+ sleep(0.1)
+ if device_file.exists():
+ raise TimeoutError(
+ f'Timeout was reached for removing PCI device {pci_addr}'
+ )
+ else:
+ raise FileNotFoundError(f'PCI device {pci_addr} does not exist')
+ rescan_file = Path('/sys/bus/pci/rescan')
+ rescan_file.write_text('1')
+ if pci_addr:
+ # wait 10 seconds max until device will be installed
+ attempts = 100
+ while not device_file.exists() and attempts:
+ attempts -= 1
+ sleep(0.1)
+ if not device_file.exists():
+ raise TimeoutError(
+ f'Timeout was reached for installing PCI device {pci_addr}')
+
+ @staticmethod
+ def get_eth_name(pci_addr: str) -> str:
+ """Find Ethernet interface name by PCI address
+
+ Args:
+ pci_addr (str): PCI address
+
+ Raises:
+ FileNotFoundError: no Ethernet interface was found
+
+ Returns:
+ str: Ethernet interface name
+ """
+ # find all PCI devices with eth* names
+ net_devs: dict[str, str] = {}
+ net_devs_dir = Path('/sys/class/net')
+ regex_filter = r'^/sys/devices/pci[\w/:\.]+/(?P<pci_addr>\w+:\w+:\w+\.\w+)/[\w/:\.]+/(?P<iface_name>eth\d+)$'
+ for dir in net_devs_dir.iterdir():
+ real_dir: str = dir.resolve().as_posix()
+ re_obj = re_fullmatch(regex_filter, real_dir)
+ if re_obj:
+ iface_name: str = re_obj.group('iface_name')
+ iface_addr: str = re_obj.group('pci_addr')
+ net_devs.update({iface_addr: iface_name})
+ # match to provided PCI address and return a name if found
+ if pci_addr in net_devs:
+ return net_devs[pci_addr]
+ # raise error if device was not found
+ raise FileNotFoundError(
+ f'PCI device {pci_addr} not found in ethernet interfaces')
+
+ @staticmethod
+ def rename_iface(name_old: str, name_new: str) -> None:
+ """Rename interface
+
+ Args:
+ name_old (str): old name
+ name_new (str): new name
+ """
+ rename_cmd: list[str] = [
+ 'ip', 'link', 'set', name_old, 'name', name_new
+ ]
+ run(rename_cmd)
diff --git a/python/vyos/xml_ref/__init__.py b/python/vyos/xml_ref/__init__.py
index 2e144ef10..bf434865d 100644
--- a/python/vyos/xml_ref/__init__.py
+++ b/python/vyos/xml_ref/__init__.py
@@ -13,8 +13,12 @@
# 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 typing import Optional, Union, TYPE_CHECKING
from vyos.xml_ref import definition
+if TYPE_CHECKING:
+ from vyos.config import ConfigDict
+
def load_reference(cache=[]):
if cache:
return cache[0]
@@ -23,11 +27,15 @@ def load_reference(cache=[]):
try:
from vyos.xml_ref.cache import reference
- xml.define(reference)
- cache.append(xml)
except Exception:
raise ImportError('no xml reference cache !!')
+ if not reference:
+ raise ValueError('empty xml reference cache !!')
+
+ xml.define(reference)
+ cache.append(xml)
+
return xml
def is_tag(path: list) -> bool:
@@ -51,6 +59,9 @@ def cli_defined(path: list, node: str, non_local=False) -> bool:
def component_version() -> dict:
return load_reference().component_version()
+def default_value(path: list) -> Optional[Union[str, list]]:
+ return load_reference().default_value(path)
+
def multi_to_list(rpath: list, conf: dict) -> dict:
return load_reference().multi_to_list(rpath, conf)
@@ -58,12 +69,15 @@ def get_defaults(path: list, get_first_key=False, recursive=False) -> dict:
return load_reference().get_defaults(path, get_first_key=get_first_key,
recursive=recursive)
-def get_config_defaults(rpath: list, conf: dict, get_first_key=False,
- recursive=False) -> dict:
+def relative_defaults(rpath: list, conf: dict, get_first_key=False,
+ recursive=False) -> dict:
- return load_reference().relative_defaults(rpath, conf=conf,
+ return load_reference().relative_defaults(rpath, conf,
get_first_key=get_first_key,
recursive=recursive)
-def merge_defaults(path: list, conf: dict) -> dict:
- return load_reference().merge_defaults(path, conf)
+def from_source(d: dict, path: list) -> bool:
+ return definition.from_source(d, path)
+
+def ext_dict_merge(source: dict, destination: Union[dict, 'ConfigDict']):
+ return definition.ext_dict_merge(source, destination)
diff --git a/python/vyos/xml_ref/definition.py b/python/vyos/xml_ref/definition.py
index 95ecc01a6..c90c5ddbc 100644
--- a/python/vyos/xml_ref/definition.py
+++ b/python/vyos/xml_ref/definition.py
@@ -13,8 +13,51 @@
# 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 typing import Union, Any
-from vyos.configdict import dict_merge
+from typing import Optional, Union, Any, TYPE_CHECKING
+
+# https://peps.python.org/pep-0484/#forward-references
+# for type 'ConfigDict'
+if TYPE_CHECKING:
+ from vyos.config import ConfigDict
+
+def set_source_recursive(o: Union[dict, str, list], b: bool):
+ d = {}
+ if not isinstance(o, dict):
+ d = {'_source': b}
+ else:
+ for k, v in o.items():
+ d[k] = set_source_recursive(v, b)
+ d |= {'_source': b}
+ return d
+
+def source_dict_merge(src: dict, dest: dict):
+ from copy import deepcopy
+ dst = deepcopy(dest)
+ from_src = {}
+
+ for key, value in src.items():
+ if key not in dst:
+ dst[key] = value
+ from_src[key] = set_source_recursive(value, True)
+ elif isinstance(src[key], dict):
+ dst[key], f = source_dict_merge(src[key], dst[key])
+ f |= {'_source': False}
+ from_src[key] = f
+
+ return dst, from_src
+
+def ext_dict_merge(src: dict, dest: Union[dict, 'ConfigDict']):
+ d, f = source_dict_merge(src, dest)
+ if hasattr(d, '_from_defaults'):
+ setattr(d, '_from_defaults', f)
+ return d
+
+def from_source(d: dict, path: list) -> bool:
+ for key in path:
+ d = d[key] if key in d else {}
+ if not d or not isinstance(d, dict):
+ return False
+ return d.get('_source', False)
class Xml:
def __init__(self):
@@ -119,14 +162,11 @@ class Xml:
def component_version(self) -> dict:
d = {}
- for k, v in self.ref['component_version']:
+ for k, v in self.ref['component_version'].items():
d[k] = int(v)
return d
def multi_to_list(self, rpath: list, conf: dict) -> dict:
- if rpath and rpath[-1] in list(conf):
- raise ValueError('rpath should be disjoint from conf keys')
-
res: Any = {}
for k in list(conf):
@@ -141,9 +181,26 @@ class Xml:
return res
- def _get_default_value(self, node: dict):
+ def _get_default_value(self, node: dict) -> Optional[str]:
return self._get_ref_node_data(node, "default_value")
+ def _get_default(self, node: dict) -> Optional[Union[str, list]]:
+ default = self._get_default_value(node)
+ if default is None:
+ return None
+ if self._is_multi_node(node):
+ return default.split()
+ return default
+
+ def default_value(self, path: list) -> Optional[Union[str, list]]:
+ d = self._get_ref_path(path)
+ default = self._get_default_value(d)
+ if default is None:
+ return None
+ if self._is_multi_node(d) or self._is_tag_node(d):
+ return default.split()
+ return default
+
def get_defaults(self, path: list, get_first_key=False, recursive=False) -> dict:
"""Return dict containing default values below path
@@ -153,18 +210,23 @@ class Xml:
'relative_defaults'
"""
res: dict = {}
+ if self.is_tag(path):
+ return res
+
d = self._get_ref_path(path)
+
+ if self._is_leaf_node(d):
+ default_value = self._get_default(d)
+ if default_value is not None:
+ return {path[-1]: default_value} if path else {}
+
for k in list(d):
if k in ('node_data', 'component_version') :
continue
- d_k = d[k]
- if self._is_leaf_node(d_k):
- default_value = self._get_default_value(d_k)
+ if self._is_leaf_node(d[k]):
+ default_value = self._get_default(d[k])
if default_value is not None:
- pos = default_value
- if self._is_multi_node(d_k) and not isinstance(pos, list):
- pos = [pos]
- res |= {k: pos}
+ res |= {k: default_value}
elif self.is_tag(path + [k]):
# tag node defaults are used as suggestion, not default value;
# should this change, append to path and continue if recursive
@@ -175,8 +237,6 @@ class Xml:
res |= pos
if res:
if get_first_key or not path:
- if not isinstance(res, dict):
- raise TypeError("Cannot get_first_key as data under node is not of type dict")
return res
return {path[-1]: res}
@@ -188,7 +248,7 @@ class Xml:
return [next(iter(c.keys()))] if c else []
try:
tmp = step(conf)
- if self.is_tag_value(path + tmp):
+ if tmp and self.is_tag_value(path + tmp):
c = conf[tmp[0]]
if not isinstance(c, dict):
raise ValueError
@@ -200,57 +260,43 @@ class Xml:
return False
return True
- def relative_defaults(self, rpath: list, conf: dict, get_first_key=False,
- recursive=False) -> dict:
- """Return dict containing defaults along paths of a config dict
- """
- if not conf:
- return self.get_defaults(rpath, get_first_key=get_first_key,
- recursive=recursive)
- if rpath and rpath[-1] in list(conf):
- conf = conf[rpath[-1]]
- if not isinstance(conf, dict):
- raise TypeError('conf at path is not of type dict')
-
- if not self._well_defined(rpath, conf):
- print('path to config dict does not define full config paths')
- return {}
-
+ def _relative_defaults(self, rpath: list, conf: dict, recursive=False) -> dict:
res: dict = {}
+ res = self.get_defaults(rpath, recursive=recursive,
+ get_first_key=True)
for k in list(conf):
- pos = self.get_defaults(rpath + [k], recursive=recursive)
- res |= pos
-
if isinstance(conf[k], dict):
- step = self.relative_defaults(rpath + [k], conf=conf[k],
- recursive=recursive)
+ step = self._relative_defaults(rpath + [k], conf=conf[k],
+ recursive=recursive)
res |= step
if res:
- if get_first_key:
- return res
return {rpath[-1]: res} if rpath else res
return {}
- def merge_defaults(self, path: list, conf: dict) -> dict:
- """Return config dict with defaults non-destructively merged
-
- This merges non-recursive defaults relative to the config dict.
+ def relative_defaults(self, path: list, conf: dict, get_first_key=False,
+ recursive=False) -> dict:
+ """Return dict containing defaults along paths of a config dict
"""
- if path[-1] in list(conf):
- config = conf[path[-1]]
- if not isinstance(config, dict):
- raise TypeError('conf at path is not of type dict')
- shift = False
- else:
- config = conf
- shift = True
-
- if not self._well_defined(path, config):
- print('path to config dict does not define config paths; conf returned unchanged')
- return conf
-
- d = self.relative_defaults(path, conf=config, get_first_key=shift)
- d = dict_merge(d, conf)
- return d
+ if not conf:
+ return self.get_defaults(path, get_first_key=get_first_key,
+ recursive=recursive)
+ if not self._well_defined(path, conf):
+ # adjust for possible overlap:
+ if path and path[-1] in list(conf):
+ conf = conf[path[-1]]
+ conf = {} if not isinstance(conf, dict) else conf
+ if not self._well_defined(path, conf):
+ print('path to config dict does not define full config paths')
+ return {}
+
+ res = self._relative_defaults(path, conf, recursive=recursive)
+
+ if get_first_key and path:
+ if res.values():
+ res = next(iter(res.values()))
+ else:
+ res = {}
+
+ return res
diff --git a/python/vyos/xml_ref/generate_cache.py b/python/vyos/xml_ref/generate_cache.py
index 792c6eea7..6a05d4608 100755
--- a/python/vyos/xml_ref/generate_cache.py
+++ b/python/vyos/xml_ref/generate_cache.py
@@ -18,10 +18,14 @@
import sys
import json
-import argparse
+from argparse import ArgumentParser
+from argparse import ArgumentTypeError
+from os import getcwd
+from os import makedirs
from os.path import join
from os.path import abspath
from os.path import dirname
+from os.path import basename
from xmltodict import parse
_here = dirname(__file__)
@@ -29,9 +33,10 @@ _here = dirname(__file__)
sys.path.append(join(_here, '..'))
from configtree import reference_tree_to_json, ConfigTreeError
-xml_cache = abspath(join(_here, 'cache.py'))
xml_cache_json = 'xml_cache.json'
xml_tmp = join('/tmp', xml_cache_json)
+pkg_cache = abspath(join(_here, 'pkg_cache'))
+ref_cache = abspath(join(_here, 'cache.py'))
node_data_fields = ("node_type", "multi", "valueless", "default_value")
@@ -45,16 +50,26 @@ def trim_node_data(cache: dict):
if isinstance(cache[k], dict):
trim_node_data(cache[k])
+def non_trivial(s):
+ if not s:
+ raise ArgumentTypeError("Argument must be non empty string")
+ return s
+
def main():
- parser = argparse.ArgumentParser(description='generate and save dict from xml defintions')
+ parser = ArgumentParser(description='generate and save dict from xml defintions')
parser.add_argument('--xml-dir', type=str, required=True,
help='transcluded xml interface-definition directory')
- parser.add_argument('--save-json-dir', type=str,
- help='directory to save json cache if needed')
- args = parser.parse_args()
-
- xml_dir = abspath(args.xml_dir)
- save_dir = abspath(args.save_json_dir) if args.save_json_dir else None
+ parser.add_argument('--package-name', type=non_trivial, default='vyos-1x',
+ help='name of current package')
+ parser.add_argument('--output-path', help='path to generated cache')
+ args = vars(parser.parse_args())
+
+ xml_dir = abspath(args['xml_dir'])
+ pkg_name = args['package_name'].replace('-','_')
+ cache_name = pkg_name + '_cache.py'
+ out_path = args['output_path']
+ path = out_path if out_path is not None else pkg_cache
+ xml_cache = abspath(join(path, cache_name))
try:
reference_tree_to_json(xml_dir, xml_tmp)
@@ -67,21 +82,30 @@ def main():
trim_node_data(d)
- if save_dir is not None:
- save_file = join(save_dir, xml_cache_json)
- with open(save_file, 'w') as f:
- f.write(json.dumps(d))
-
syntax_version = join(xml_dir, 'xml-component-version.xml')
- with open(syntax_version) as f:
- content = f.read()
+ try:
+ with open(syntax_version) as f:
+ component = f.read()
+ except FileNotFoundError:
+ if pkg_name != 'vyos_1x':
+ component = ''
+ else:
+ print("\nWARNING: missing xml-component-version.xml\n")
+ sys.exit(1)
- parsed = parse(content)
- converted = parsed['interfaceDefinition']['syntaxVersion']
+ if component:
+ parsed = parse(component)
+ else:
+ parsed = None
version = {}
- for i in converted:
- tmp = {i['@component']: i['@version']}
- version |= tmp
+ # addon package definitions may have empty (== 0) version info
+ if parsed is not None and parsed['interfaceDefinition'] is not None:
+ converted = parsed['interfaceDefinition']['syntaxVersion']
+ if not isinstance(converted, list):
+ converted = [converted]
+ for i in converted:
+ tmp = {i['@component']: i['@version']}
+ version |= tmp
version = {"component_version": version}
@@ -90,5 +114,7 @@ def main():
with open(xml_cache, 'w') as f:
f.write(f'reference = {str(d)}')
+ print(cache_name)
+
if __name__ == '__main__':
main()
diff --git a/python/vyos/xml_ref/pkg_cache/__init__.py b/python/vyos/xml_ref/pkg_cache/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/python/vyos/xml_ref/pkg_cache/__init__.py
diff --git a/python/vyos/xml_ref/update_cache.py b/python/vyos/xml_ref/update_cache.py
new file mode 100755
index 000000000..0842bcbe9
--- /dev/null
+++ b/python/vyos/xml_ref/update_cache.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+import os
+from copy import deepcopy
+from generate_cache import pkg_cache
+from generate_cache import ref_cache
+
+def dict_merge(source, destination):
+ dest = deepcopy(destination)
+
+ for key, value in source.items():
+ if key not in dest:
+ dest[key] = value
+ elif isinstance(source[key], dict):
+ dest[key] = dict_merge(source[key], dest[key])
+
+ return dest
+
+def main():
+ res = {}
+ cache_dir = os.path.basename(pkg_cache)
+ for mod in os.listdir(pkg_cache):
+ mod = os.path.splitext(mod)[0]
+ if not mod.endswith('_cache'):
+ continue
+ d = getattr(__import__(f'{cache_dir}.{mod}', fromlist=[mod]), 'reference')
+ if mod == 'vyos_1x_cache':
+ res = dict_merge(res, d)
+ else:
+ res = dict_merge(d, res)
+
+ with open(ref_cache, 'w') as f:
+ f.write(f'reference = {str(res)}')
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/update-configd-include-file b/scripts/update-configd-include-file
index 6615e21ff..ca23408f0 100755
--- a/scripts/update-configd-include-file
+++ b/scripts/update-configd-include-file
@@ -31,7 +31,7 @@ from inspect import signature, getsource
from vyos.defaults import directories
from vyos.version import get_version
-from vyos.util import cmd
+from vyos.utils.process import cmd
# Defaults
diff --git a/smoketest/configs/dialup-router-complex b/smoketest/configs/dialup-router-complex
index 909e6d17b..aa9837fe9 100644
--- a/smoketest/configs/dialup-router-complex
+++ b/smoketest/configs/dialup-router-complex
@@ -1094,6 +1094,10 @@ firewall {
adjust-mss 1452
adjust-mss6 1432
}
+ interface eth0.10 {
+ adjust-mss 1320
+ adjust-mss6 1300
+ }
}
receive-redirects disable
send-redirects enable
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index 471bdaffb..989028f64 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,9 @@ from configparser import ConfigParser
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
-from vyos.util import cmd
-from vyos.util import get_half_cpus
-from vyos.util import process_named_running
+from vyos.utils.system import get_half_cpus
+from vyos.utils.process import process_named_running
+from vyos.utils.process import cmd
class BasicAccelPPPTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 348741715..820024dc9 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -26,17 +26,24 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
+from vyos.defaults import directories
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
-from vyos.util import read_file
-from vyos.util import cmd
-from vyos.util import dict_search
-from vyos.util import process_named_running
-from vyos.util import get_interface_config
-from vyos.validate import is_intf_addr_assigned
-from vyos.validate import is_ipv6_link_local
+from vyos.utils.file import read_file
+from vyos.utils.dict import dict_search
+from vyos.utils.process import process_named_running
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import get_interface_vrf
+from vyos.utils.process import cmd
+from vyos.utils.network import is_intf_addr_assigned
+from vyos.utils.network import is_ipv6_link_local
from vyos.xml_ref import cli_defined
+dhclient_base_dir = directories['isc_dhclient_dir']
+dhclient_process_name = 'dhclient'
+dhcp6c_base_dir = directories['dhcp6_client_dir']
+dhcp6c_process_name = 'dhcp6c'
+
def is_mirrored_to(interface, mirror_if, qdisc):
"""
Ask TC if we are mirroring traffic to a discrete interface.
@@ -66,6 +73,7 @@ class BasicInterfaceTest:
_test_ipv6_pd = False
_test_ipv6_dhcpc6 = False
_test_mirror = False
+ _test_vrf = False
_base_path = []
_options = {}
@@ -93,6 +101,7 @@ class BasicInterfaceTest:
cls._test_ipv6_dhcpc6 = cli_defined(cls._base_path, 'dhcpv6-options')
cls._test_ipv6_pd = cli_defined(cls._base_path + ['dhcpv6-options'], 'pd')
cls._test_mtu = cli_defined(cls._base_path, 'mtu')
+ cls._test_vrf = cli_defined(cls._base_path, 'vrf')
# Setup mirror interfaces for SPAN (Switch Port Analyzer)
for span in cls._mirror_interfaces:
@@ -137,8 +146,6 @@ class BasicInterfaceTest:
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://vyos.dev/T2767
@@ -151,6 +158,99 @@ class BasicInterfaceTest:
flags = read_file(f'/sys/class/net/{interface}/flags')
self.assertEqual(int(flags, 16) & 1, 0)
+ def test_dhcp_client_options(self):
+ if not self._test_dhcp or not self._test_vrf:
+ self.skipTest('not supported')
+
+ distance = '100'
+
+ for interface in self._interfaces:
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
+ self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance])
+
+ self.cli_commit()
+
+ for interface in self._interfaces:
+ # Check if dhclient process runs
+ dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface)
+ self.assertTrue(dhclient_pid)
+
+ dhclient_config = read_file(f'{dhclient_base_dir}/dhclient_{interface}.conf')
+ self.assertIn('request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config)
+ self.assertIn('require subnet-mask;', dhclient_config)
+
+ # and the commandline has the appropriate options
+ cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
+ self.assertIn(f'-e\x00IF_METRIC={distance}', cmdline)
+
+ def test_dhcp_vrf(self):
+ if not self._test_dhcp or not self._test_vrf:
+ self.skipTest('not supported')
+
+ vrf_name = 'purple4'
+ self.cli_set(['vrf', 'name', vrf_name, 'table', '65000'])
+
+ for interface in self._interfaces:
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
+ self.cli_set(self._base_path + [interface, 'vrf', vrf_name])
+
+ self.cli_commit()
+
+ # Validate interface state
+ for interface in self._interfaces:
+ tmp = get_interface_vrf(interface)
+ self.assertEqual(tmp, vrf_name)
+
+ # Check if dhclient process runs
+ dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface)
+ self.assertTrue(dhclient_pid)
+ # .. inside the appropriate VRF instance
+ vrf_pids = cmd(f'ip vrf pids {vrf_name}')
+ self.assertIn(str(dhclient_pid), vrf_pids)
+ # and the commandline has the appropriate options
+ cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
+ self.assertIn('-e\x00IF_METRIC=210', cmdline) # 210 is the default value
+
+ self.cli_delete(['vrf', 'name', vrf_name])
+
+ def test_dhcpv6_vrf(self):
+ if not self._test_ipv6_dhcpc6 or not self._test_vrf:
+ self.skipTest('not supported')
+
+ vrf_name = 'purple6'
+ self.cli_set(['vrf', 'name', vrf_name, 'table', '65001'])
+
+ # 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:
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'address', 'dhcpv6'])
+ self.cli_set(self._base_path + [interface, 'vrf', vrf_name])
+
+ self.cli_commit()
+
+ # Validate interface state
+ for interface in self._interfaces:
+ tmp = get_interface_vrf(interface)
+ self.assertEqual(tmp, vrf_name)
+
+ # Check if dhclient process runs
+ tmp = process_named_running(dhcp6c_process_name, cmdline=interface)
+ self.assertTrue(tmp)
+ # .. inside the appropriate VRF instance
+ vrf_pids = cmd(f'ip vrf pids {vrf_name}')
+ self.assertIn(str(tmp), vrf_pids)
+
+ self.cli_delete(['vrf', 'name', vrf_name])
+
def test_span_mirror(self):
if not self._mirror_interfaces:
self.skipTest('not supported')
@@ -743,6 +843,8 @@ class BasicInterfaceTest:
mss = '1400'
dad_transmits = '10'
+ accept_dad = '0'
+ source_validation = 'strict'
for interface in self._interfaces:
path = self._base_path + [interface]
@@ -753,12 +855,18 @@ class BasicInterfaceTest:
if cli_defined(self._base_path + ['ipv6'], 'adjust-mss'):
self.cli_set(path + ['ipv6', 'adjust-mss', mss])
+ if cli_defined(self._base_path + ['ipv6'], 'accept-dad'):
+ self.cli_set(path + ['ipv6', 'accept-dad', accept_dad])
+
if cli_defined(self._base_path + ['ipv6'], 'dup-addr-detect-transmits'):
self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits])
if cli_defined(self._base_path + ['ipv6'], 'disable-forwarding'):
self.cli_set(path + ['ipv6', 'disable-forwarding'])
+ if cli_defined(self._base_path + ['ipv6'], 'source-validation'):
+ self.cli_set(path + ['ipv6', 'source-validation', source_validation])
+
self.cli_commit()
for interface in self._interfaces:
@@ -770,6 +878,10 @@ class BasicInterfaceTest:
if line.startswith(base_options):
self.assertIn(f'tcp option maxseg size set {mss}', line)
+ if cli_defined(self._base_path + ['ipv6'], 'accept-dad'):
+ tmp = read_file(f'{proc_base}/accept_dad')
+ self.assertEqual(accept_dad, tmp)
+
if cli_defined(self._base_path + ['ipv6'], 'dup-addr-detect-transmits'):
tmp = read_file(f'{proc_base}/dad_transmits')
self.assertEqual(dad_transmits, tmp)
@@ -778,6 +890,14 @@ class BasicInterfaceTest:
tmp = read_file(f'{proc_base}/forwarding')
self.assertEqual('0', tmp)
+ if cli_defined(self._base_path + ['ipv6'], 'source-validation'):
+ base_options = f'iifname "{interface}"'
+ out = cmd('sudo nft list chain ip6 raw vyos_rpfilter')
+ for line in out.splitlines():
+ if line.startswith(base_options):
+ self.assertIn('fib saddr . iif oif 0', line)
+ self.assertIn('drop', line)
+
def test_dhcpv6_client_options(self):
if not self._test_ipv6_dhcpc6:
self.skipTest('not supported')
@@ -791,6 +911,7 @@ class BasicInterfaceTest:
# Enable DHCPv6 client
self.cli_set(path + ['address', 'dhcpv6'])
+ self.cli_set(path + ['dhcpv6-options', 'no-release'])
self.cli_set(path + ['dhcpv6-options', 'rapid-commit'])
self.cli_set(path + ['dhcpv6-options', 'parameters-only'])
self.cli_set(path + ['dhcpv6-options', 'duid', duid])
@@ -801,7 +922,7 @@ class BasicInterfaceTest:
duid_base = 10
for interface in self._interfaces:
duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base)
- dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf')
+ dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf')
self.assertIn(f'interface {interface} ' + '{', dhcpc6_config)
self.assertIn(f' request domain-name-servers;', dhcpc6_config)
self.assertIn(f' request domain-name;', dhcpc6_config)
@@ -812,8 +933,12 @@ class BasicInterfaceTest:
self.assertIn('};', dhcpc6_config)
duid_base += 1
- # Check for running process
- self.assertTrue(process_named_running('dhcp6c'))
+ # Better ask the process about it's commandline in the future
+ pid = process_named_running(dhcp6c_process_name, cmdline=interface)
+ self.assertTrue(pid)
+
+ dhcp6c_options = read_file(f'/proc/{pid}/cmdline')
+ self.assertIn('-n', dhcp6c_options)
def test_dhcpv6pd_auto_sla_id(self):
if not self._test_ipv6_pd:
@@ -849,7 +974,7 @@ class BasicInterfaceTest:
self.cli_commit()
for interface in self._interfaces:
- dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf')
+ dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf')
# verify DHCPv6 prefix delegation
self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config)
@@ -867,8 +992,8 @@ class BasicInterfaceTest:
# increment interface address
address = str(int(address) + 1)
- # Check for running process
- self.assertTrue(process_named_running('dhcp6c'))
+ # Check for running process
+ self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface))
for delegatee in delegatees:
# we can already cleanup the test delegatee interface here
@@ -917,7 +1042,7 @@ class BasicInterfaceTest:
for interface in self._interfaces:
address = '1'
sla_id = '1'
- dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf')
+ dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf')
# verify DHCPv6 prefix delegation
self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config)
@@ -934,7 +1059,7 @@ class BasicInterfaceTest:
address = str(int(address) + 1)
# Check for running process
- self.assertTrue(process_named_running('dhcp6c', interface))
+ self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface))
for delegatee in delegatees:
# we can already cleanup the test delegatee interface here
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index 7cfb53045..f694f539d 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,6 +14,7 @@
import os
import unittest
+import paramiko
from time import sleep
from typing import Type
@@ -22,8 +23,8 @@ from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos import ConfigError
from vyos.defaults import commit_lock
-from vyos.util import cmd
-from vyos.util import run
+from vyos.utils.process import cmd
+from vyos.utils.process import run
save_config = '/tmp/vyos-smoketest-save'
@@ -87,6 +88,19 @@ class VyOSUnitTestSHIM:
pprint.pprint(out)
return out
+ @staticmethod
+ def ssh_send_cmd(command, username, password, hostname='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=hostname, 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
+
# standard construction; typing suggestion: https://stackoverflow.com/a/70292317
def ignore_warning(warning: Type[Warning]):
import warnings
diff --git a/smoketest/scripts/cli/test_configd_init.py b/smoketest/scripts/cli/test_configd_init.py
index 5dec89963..245c03824 100755
--- a/smoketest/scripts/cli/test_configd_init.py
+++ b/smoketest/scripts/cli/test_configd_init.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,7 +17,8 @@
import unittest
from time import sleep
-from vyos.util import cmd, is_systemd_service_running
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.process import cmd
class TestConfigdInit(unittest.TestCase):
def setUp(self):
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index 902156ee6..b43c05fae 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.py
@@ -21,9 +21,9 @@ import json
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
base_path = ['container']
cont_image = 'busybox:stable' # busybox is included in vyos-build
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 99d3b3ca1..ee6ccb710 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -22,8 +22,8 @@ from time import sleep
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import run
+from vyos.utils.process import cmd
+from vyos.utils.process import run
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'default': '0', 'test_value': 'disable'},
@@ -78,6 +78,17 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
break
self.assertTrue(not matched if inverse else matched, msg=search)
+ def verify_nftables_chain(self, nftables_search, table, chain, inverse=False, args=''):
+ nftables_output = cmd(f'sudo nft {args} list chain {table} {chain}')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(not matched if inverse else matched, msg=search)
+
def wait_for_domain_resolver(self, table, set_name, element, max_wait=10):
# Resolver no longer blocks commit, need to wait for daemon to populate set
count = 0
@@ -90,19 +101,19 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
return False
def test_geoip(self):
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'drop'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'inverse-match'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'inverse-match'])
self.cli_commit()
nftables_search = [
- ['ip saddr @GEOIP_CC_smoketest_1', 'drop'],
- ['ip saddr != @GEOIP_CC_smoketest_2', 'return']
+ ['ip saddr @GEOIP_CC_name_smoketest_1', 'drop'],
+ ['ip saddr != @GEOIP_CC_name_smoketest_2', 'accept']
]
# -t prevents 1000+ GeoIP elements being returned
@@ -127,36 +138,33 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'eth0'])
self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'vtun0'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
- 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_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(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'action', 'accept'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'outbound-interface', 'interface-group', 'smoketest_interface'])
-
- self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'destination', 'address', '172.16.10.10'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '2', 'source', 'group', 'mac-group', 'smoketest_mac'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'outbound-interface', 'interface-group', '!smoketest_interface'])
self.cli_commit()
self.wait_for_domain_resolver('ip vyos_filter', 'D_smoketest_domain', '192.0.2.5')
nftables_search = [
- ['iifname "eth0"', 'jump NAME_smoketest'],
- ['ip saddr @N_smoketest_network', 'ip daddr 172.16.10.10', 'th dport @P_smoketest_port', 'return'],
+ ['ip saddr @N_smoketest_network', 'ip daddr 172.16.10.10', 'th dport @P_smoketest_port', 'accept'],
['elements = { 172.16.99.0/24 }'],
['elements = { 53, 123 }'],
- ['ether saddr @M_smoketest_mac', 'return'],
+ ['ether saddr @M_smoketest_mac', 'accept'],
['elements = { 00:01:02:03:04:05 }'],
['set D_smoketest_domain'],
['elements = { 192.0.2.5, 192.0.2.8,'],
['192.0.2.10, 192.0.2.11 }'],
- ['ip saddr @D_smoketest_domain', 'return'],
- ['oifname @I_smoketest_interface', 'return']
+ ['ip saddr @D_smoketest_domain', 'accept'],
+ ['oifname != @I_smoketest_interface', 'accept']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -170,12 +178,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port1', 'port', '123'])
self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port1', 'include', 'smoketest_port'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network1'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port1'])
- self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
-
- self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network1'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port1'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
self.cli_commit()
@@ -187,8 +193,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['firewall', 'group', 'network-group', 'smoketest_network', 'include', 'smoketest_network1'])
nftables_search = [
- ['iifname "eth0"', 'jump NAME_smoketest'],
- ['ip saddr @N_smoketest_network1', 'th dport @P_smoketest_port1', 'return'],
+ ['ip saddr @N_smoketest_network1', 'th dport @P_smoketest_port1', 'accept'],
['elements = { 172.16.99.0/24, 172.16.101.0/24 }'],
['elements = { 53, 123 }']
]
@@ -198,65 +203,80 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
def test_ipv4_basic_rules(self):
name = 'smoketest'
interface = 'eth0'
+ interface_inv = '!eth0'
interface_wc = 'l2tp*'
mss_range = '501-1460'
conn_mark = '555'
- self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'enable-default-log'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'log', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-options', 'level', 'debug'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'ttl', 'eq', '15'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'protocol', 'tcp'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'destination', 'port', '8888'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'log', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-options', 'level', 'err'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'ttl', 'gt', '102'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'protocol', 'tcp'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'destination', 'port', '22'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'limit', 'rate', '5/minute'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'log', 'disable'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'protocol', 'tcp'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'destination', 'port', '22'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'count', '10'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'time', 'minute'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'packet-type', 'host'])
- self.cli_set(['firewall', 'name', name, 'rule', '5', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '5', 'protocol', 'tcp'])
- self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'flags', 'syn'])
- self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'mss', mss_range])
- self.cli_set(['firewall', 'name', name, 'rule', '5', 'packet-type', 'broadcast'])
- self.cli_set(['firewall', 'name', name, 'rule', '5', 'inbound-interface', 'interface-name', interface])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'return'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'protocol', 'gre'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'outbound-interface', 'interface-name', interface])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'connection-mark', conn_mark])
-
- self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
- self.cli_set(['firewall', 'interface', interface_wc, 'in', 'name', name])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'log-options', 'level', 'debug'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'ttl', 'eq', '15'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'action', 'reject'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'destination', 'port', '8888'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'log-options', 'level', 'err'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'ttl', 'gt', '102'])
+
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'destination', 'port', '22'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'limit', 'rate', '5/minute'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'log', 'disable'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'destination', 'port', '22'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'recent', 'count', '10'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'recent', 'time', 'minute'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'packet-type', 'host'])
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'tcp', 'flags', 'syn'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'tcp', 'mss', mss_range])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'packet-type', 'broadcast'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'inbound-interface', 'interface-name', interface_wc])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'action', 'return'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'protocol', 'gre'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'connection-mark', conn_mark])
+
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'default-action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'protocol', 'gre'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'outbound-interface', 'interface-name', interface_inv])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '6', 'action', 'return'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '6', 'protocol', 'icmp'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '6', 'connection-mark', conn_mark])
self.cli_commit()
mark_hex = "{0:#010x}".format(int(conn_mark))
nftables_search = [
- [f'iifname "{interface}"', f'jump NAME_{name}'],
- [f'iifname "{interface_wc}"', f'jump NAME_{name}'],
- ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'return'],
+ ['chain VYOS_FORWARD_filter'],
+ ['type filter hook forward priority filter; policy drop;'],
+ ['tcp dport 22', 'limit rate 5/minute', 'accept'],
+ ['tcp dport 22', 'add @RECENT_FWD_filter_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'],
+ ['chain VYOS_INPUT_filter'],
+ ['type filter hook input priority filter; policy accept;'],
+ ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface_wc}"', 'meta pkttype broadcast', 'accept'],
+ ['meta l4proto gre', f'ct mark {mark_hex}', 'return'],
+ ['chain VYOS_OUTPUT_filter'],
+ ['type filter hook output priority filter; policy accept;'],
+ ['meta l4proto gre', f'oifname != "{interface}"', 'drop'],
+ ['meta l4proto icmp', f'ct mark {mark_hex}', 'return'],
+ ['chain NAME_smoketest'],
+ ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'accept'],
['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'],
- ['tcp dport 22', 'limit rate 5/minute', 'return'],
- ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'],
- ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'],
- ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"', 'meta pkttype broadcast'],
- ['meta l4proto gre', f'oifname "{interface}"', f'ct mark {mark_hex}', 'return']
+ ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -266,55 +286,54 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
name2 = 'smoketest-adv2'
interface = 'eth0'
- self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'enable-default-log'])
-
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '64'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '512'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '1024'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '17'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '52'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'log', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'group', '66'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666'])
- self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000'])
-
- self.cli_set(['firewall', 'name', name, 'rule', '7', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length', '1-30000'])
- self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length-exclude', '60000-65535'])
- self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp', '3-11'])
- self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp-exclude', '21-25'])
-
- self.cli_set(['firewall', 'name', name2, 'default-action', 'jump'])
- self.cli_set(['firewall', 'name', name2, 'default-jump-target', name])
- self.cli_set(['firewall', 'name', name2, 'enable-default-log'])
- self.cli_set(['firewall', 'name', name2, 'rule', '1', 'source', 'address', '198.51.100.1'])
- self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump'])
- self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name])
-
- self.cli_set(['firewall', 'name', name2, 'rule', '2', 'protocol', 'tcp'])
- self.cli_set(['firewall', 'name', name2, 'rule', '2', 'action', 'queue'])
- self.cli_set(['firewall', 'name', name2, 'rule', '2', 'queue', '3'])
- self.cli_set(['firewall', 'name', name2, 'rule', '3', 'protocol', 'udp'])
- self.cli_set(['firewall', 'name', name2, 'rule', '3', 'action', 'queue'])
- self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue-options', 'fanout'])
- self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue-options', 'bypass'])
- self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue', '0-15'])
-
- self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log'])
+
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'packet-length', '64'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'packet-length', '512'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'packet-length', '1024'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'dscp', '17'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'dscp', '52'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log-options', 'group', '66'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000'])
+
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '7', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '7', 'packet-length', '1-30000'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '7', 'packet-length-exclude', '60000-65535'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '7', 'dscp', '3-11'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '7', 'dscp-exclude', '21-25'])
+
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'source', 'address', '198.51.100.1'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'jump'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'jump-target', name])
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'action', 'queue'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'queue', '3'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '3', 'protocol', 'udp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '3', 'action', 'queue'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '3', 'queue-options', 'fanout'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '3', 'queue-options', 'bypass'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '3', 'queue', '0-15'])
self.cli_commit()
nftables_search = [
- [f'iifname "{interface}"', f'jump NAME_{name}'],
- ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'return'],
- ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'],
- [f'log prefix "[{name}-default-D]"', 'drop'],
+ ['chain VYOS_FORWARD_filter'],
+ ['type filter hook forward priority filter; policy drop;'],
['ip saddr 198.51.100.1', f'jump NAME_{name}'],
- [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}'],
+ ['chain VYOS_INPUT_filter'],
+ ['type filter hook input priority filter; policy accept;'],
[f'meta l4proto tcp','queue to 3'],
- [f'meta l4proto udp','queue flags bypass,fanout to 0-15']
+ [f'meta l4proto udp','queue flags bypass,fanout to 0-15'],
+ [f'chain NAME_{name}'],
+ ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'accept'],
+ ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'accept'],
+ [f'log prefix "[{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -325,22 +344,20 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'address-group', 'mask_group', 'address', '1.1.1.1'])
- self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'enable-default-log'])
-
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address-mask', '0.0.255.255'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address', '!0.0.3.4'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address-mask', '0.0.255.255'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address-mask', '0.0.255.255'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'address-mask', '0.0.255.255'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'source', 'address', '!0.0.3.4'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'source', 'address-mask', '0.0.255.255'])
- self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'source', 'address-mask', '0.0.255.255'])
self.cli_commit()
@@ -357,34 +374,46 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
name = 'v6-smoketest'
interface = 'eth0'
- self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'accept'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'source', 'address', '2002::1'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '2002::1:1'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log', 'enable'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-options', 'level', 'crit'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'source', 'address', '2002::1'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'destination', 'address', '2002::1:1'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'log-options', 'level', 'crit'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'reject'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'protocol', 'tcp_udp'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'destination', 'port', '8888'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'inbound-interface', 'interface-name', interface])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'default-action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'action', 'reject'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'destination', 'port', '8888'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'inbound-interface', 'interface-name', interface])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'return'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'protocol', 'gre'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'outbound-interface', 'interface-name', interface])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'action', 'return'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'protocol', 'gre'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'outbound-interface', 'interface-name', interface])
- self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'protocol', 'udp'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'source', 'address', '2002::1:2'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'inbound-interface', 'interface-name', interface])
self.cli_commit()
nftables_search = [
- [f'iifname "{interface}"', f'jump NAME6_{name}'],
- ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'return'],
+ ['chain VYOS_IPV6_FORWARD_filter'],
+ ['type filter hook forward priority filter; policy accept;'],
['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'],
+ ['chain VYOS_IPV6_INPUT_filter'],
+ ['type filter hook input priority filter; policy accept;'],
+ ['meta l4proto udp', 'ip6 saddr 2002::1:2', f'iifname "{interface}"', 'accept'],
+ ['chain VYOS_IPV6_OUTPUT_filter'],
+ ['type filter hook output priority filter; policy drop;'],
['meta l4proto gre', f'oifname "{interface}"', 'return'],
- ['smoketest default-action', f'log prefix "[{name}-default-D]"', 'drop']
+ [f'chain NAME6_{name}'],
+ ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'accept'],
+ [f'"{name} default-action drop"', f'log prefix "[{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
@@ -394,40 +423,39 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
name2 = 'v6-smoketest-adv2'
interface = 'eth0'
- self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'accept'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '65'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '513'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '1025'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '18'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '53'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'packet-length', '65'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'packet-length', '513'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'packet-length', '1025'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'dscp', '18'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'dscp', '53'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'action', 'accept'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length', '1-1999'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length-exclude', '60000-65535'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp', '4-14'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp-exclude', '31-35'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'packet-length', '1-1999'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'packet-length-exclude', '60000-65535'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'dscp', '4-14'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'dscp-exclude', '31-35'])
- self.cli_set(['firewall', 'ipv6-name', name2, 'default-action', 'jump'])
- self.cli_set(['firewall', 'ipv6-name', name2, 'default-jump-target', name])
- self.cli_set(['firewall', 'ipv6-name', name2, 'enable-default-log'])
- self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'source', 'address', '2001:db8::/64'])
- self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'action', 'jump'])
- self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'jump-target', name])
-
- self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'default-action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'source', 'address', '2001:db8::/64'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'action', 'jump'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'jump-target', name])
self.cli_commit()
nftables_search = [
- [f'iifname "{interface}"', f'jump NAME6_{name}'],
- ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'return'],
- ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'return'],
- [f'log prefix "[{name}-default-D]"', 'drop'],
+ ['chain VYOS_IPV6_FORWARD_filter'],
+ ['type filter hook forward priority filter; policy accept;'],
+ ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'accept'],
+ ['chain VYOS_IPV6_INPUT_filter'],
+ ['type filter hook input priority filter; policy accept;'],
['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'],
- [f'log prefix "[{name2}-default-J]"', f'jump NAME6_{name}']
+ [f'chain NAME6_{name}'],
+ ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'accept'],
+ [f'log prefix "[{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
@@ -438,22 +466,20 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'ipv6-address-group', 'mask_group', 'address', '::beef'])
- self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
-
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'drop'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address-mask', '::ffff:ffff:ffff:ffff'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'accept'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address', '!::aaaa:bbbb:cccc:dddd'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'destination', 'address-mask', '::ffff:ffff:ffff:ffff'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'drop'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
- self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '2', 'source', 'address', '!::aaaa:bbbb:cccc:dddd'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '2', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
- self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
self.cli_commit()
@@ -465,57 +491,62 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
- def test_state_policy(self):
- self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
- self.cli_set(['firewall', 'state-policy', 'related', 'action', 'accept'])
- self.cli_set(['firewall', 'state-policy', 'invalid', 'action', 'drop'])
-
- self.cli_commit()
-
- chains = {
- 'ip vyos_filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
- 'ip6 vyos_filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
- }
-
- for table in ['ip vyos_filter', 'ip6 vyos_filter']:
- for chain in chains[table]:
- nftables_output = cmd(f'sudo nft list chain {table} {chain}')
- self.assertTrue('jump VYOS_STATE_POLICY' in nftables_output)
-
def test_ipv4_state_and_status_rules(self):
name = 'smoketest-state'
interface = 'eth0'
- self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'state', 'established', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '1', 'state', 'related', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject'])
- self.cli_set(['firewall', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'state', 'new', 'enable'])
-
- self.cli_set(['firewall', 'name', name, 'rule', '3', 'connection-status', 'nat', 'destination'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'action', 'accept'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'new', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
- self.cli_set(['firewall', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
-
- self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'established', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'related', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'action', 'reject'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'state', 'new', 'enable'])
+
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'connection-status', 'nat', 'destination'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'state', 'new', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
self.cli_commit()
nftables_search = [
- [f'iifname "{interface}"', f'jump NAME_{name}'],
- ['ct state { established, related }', 'return'],
+ ['ct state { established, related }', 'accept'],
['ct state invalid', 'reject'],
- ['ct state new', 'ct status dnat', 'return'],
- ['ct state { established, new }', 'ct status snat', 'return'],
+ ['ct state new', 'ct status dnat', 'accept'],
+ ['ct state { established, new }', 'ct status snat', 'accept'],
['drop', f'comment "{name} default-action drop"']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
+ # Check conntrack
+ self.verify_nftables_chain([['accept']], 'raw', 'FW_CONNTRACK')
+ self.verify_nftables_chain([['return']], 'ip6 raw', 'FW_CONNTRACK')
+
+ def test_source_validation(self):
+ # Strict
+ self.cli_set(['firewall', 'global-options', 'source-validation', 'strict'])
+ self.cli_commit()
+
+ nftables_strict_search = [
+ ['fib saddr . iif oif 0', 'drop']
+ ]
+
+ self.verify_nftables(nftables_strict_search, 'inet vyos_global_rpfilter')
+
+ # Loose
+ self.cli_set(['firewall', 'global-options', 'source-validation', 'loose'])
+ self.cli_commit()
+
+ nftables_loose_search = [
+ ['fib saddr oif 0', 'drop']
+ ]
+
+ self.verify_nftables(nftables_loose_search, 'inet vyos_global_rpfilter')
+
def test_sysfs(self):
for name, conf in sysfs_config.items():
paths = glob(conf['sysfs'])
@@ -523,7 +554,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertEqual(f.read().strip(), conf['default'], msg=path)
- self.cli_set(['firewall', name.replace("_", "-"), conf['test_value']])
+ self.cli_set(['firewall', 'global-options', name.replace("_", "-"), conf['test_value']])
self.cli_commit()
@@ -533,35 +564,5 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
- def test_zone_basic(self):
- self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
- self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
- self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
- self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
- self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
-
- self.cli_commit()
-
- nftables_search = [
- ['chain VZONE_smoketest-eth0'],
- ['chain VZONE_smoketest-local_IN'],
- ['chain VZONE_smoketest-local_OUT'],
- ['oifname "eth0"', 'jump VZONE_smoketest-eth0'],
- ['jump VZONE_smoketest-local_IN'],
- ['jump VZONE_smoketest-local_OUT'],
- ['iifname "eth0"', 'jump NAME_smoketest'],
- ['oifname "eth0"', 'jump NAME_smoketest']
- ]
-
- nftables_output = cmd('sudo nft list table ip vyos_filter')
-
- for search in nftables_search:
- matched = False
- for line in nftables_output.split("\n"):
- if all(item in line for item in search):
- matched = True
- break
- self.assertTrue(matched)
-
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_ha_virtual_server.py b/smoketest/scripts/cli/test_ha_virtual_server.py
index e3a91283e..51ccfa4df 100755
--- a/smoketest/scripts/cli/test_ha_virtual_server.py
+++ b/smoketest/scripts/cli/test_ha_virtual_server.py
@@ -20,9 +20,9 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig.vrrp import VRRP
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
from vyos.template import inc_ip
PROCESS_NAME = 'keepalived'
@@ -47,6 +47,7 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
delay = '10'
method = 'nat'
persistence_timeout = '600'
+ vs = 'serv-one'
vip = '203.0.113.111'
vport = '2222'
rservers = ['192.0.2.21', '192.0.2.22', '192.0.2.23']
@@ -56,21 +57,23 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
vserver_base = base_path + ['virtual-server']
- self.cli_set(vserver_base + [vip, 'algorithm', algo])
- self.cli_set(vserver_base + [vip, 'delay-loop', delay])
- self.cli_set(vserver_base + [vip, 'forward-method', method])
- self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
- self.cli_set(vserver_base + [vip, 'port', vport])
- self.cli_set(vserver_base + [vip, 'protocol', proto])
+ self.cli_set(vserver_base + [vs, 'address', vip])
+ self.cli_set(vserver_base + [vs, 'algorithm', algo])
+ self.cli_set(vserver_base + [vs, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vs, 'forward-method', method])
+ self.cli_set(vserver_base + [vs, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vs, 'port', vport])
+ self.cli_set(vserver_base + [vs, 'protocol', proto])
for rs in rservers:
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'port', rport])
# commit changes
self.cli_commit()
config = read_file(KEEPALIVED_CONF)
+ self.assertIn(f'virtual_server {vip} {vport}', config)
self.assertIn(f'delay_loop {delay}', config)
self.assertIn(f'lb_algo lc', config)
self.assertIn(f'lb_kind {method.upper()}', config)
@@ -86,6 +89,7 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
delay = '15'
method = 'nat'
persistence_timeout = '300'
+ vs = 'serv-two'
vip = '203.0.113.222'
vport = '22322'
rservers = ['192.0.2.11', '192.0.2.12']
@@ -107,15 +111,16 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(vrrp_base + [group, 'vrid', vrid])
# Virtual-server config
- self.cli_set(vserver_base + [vip, 'algorithm', algo])
- self.cli_set(vserver_base + [vip, 'delay-loop', delay])
- self.cli_set(vserver_base + [vip, 'forward-method', method])
- self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
- self.cli_set(vserver_base + [vip, 'port', vport])
- self.cli_set(vserver_base + [vip, 'protocol', proto])
+ self.cli_set(vserver_base + [vs, 'address', vip])
+ self.cli_set(vserver_base + [vs, 'algorithm', algo])
+ self.cli_set(vserver_base + [vs, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vs, 'forward-method', method])
+ self.cli_set(vserver_base + [vs, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vs, 'port', vport])
+ self.cli_set(vserver_base + [vs, 'protocol', proto])
for rs in rservers:
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'port', rport])
# commit changes
self.cli_commit()
@@ -131,6 +136,7 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'preempt_delay 0', config) # default value
# Keepalived virtual-server
+ self.assertIn(f'virtual_server {vip} {vport}', config)
self.assertIn(f'delay_loop {delay}', config)
self.assertIn(f'lb_algo lc', config)
self.assertIn(f'lb_kind {method.upper()}', config)
diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py
index 3a4de2d8d..98259d830 100755
--- a/smoketest/scripts/cli/test_ha_vrrp.py
+++ b/smoketest/scripts/cli/test_ha_vrrp.py
@@ -20,9 +20,9 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig.vrrp import VRRP
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
from vyos.template import inc_ip
PROCESS_NAME = 'keepalived'
@@ -96,6 +96,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
group_garp_master_delay = '12'
group_garp_master_repeat = '13'
group_garp_master_refresh = '14'
+ vrrp_version = '3'
for group in groups:
vlan_id = group.lstrip('VLAN')
@@ -133,6 +134,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
self.cli_set(global_param_base + ['garp', 'master-repeat', f'{garp_master_repeat}'])
self.cli_set(global_param_base + ['garp', 'master-refresh', f'{garp_master_refresh}'])
self.cli_set(global_param_base + ['garp', 'master-refresh-repeat', f'{garp_master_refresh_repeat}'])
+ self.cli_set(global_param_base + ['version', vrrp_version])
# commit changes
self.cli_commit()
@@ -145,6 +147,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'vrrp_garp_master_repeat {garp_master_repeat}', config)
self.assertIn(f'vrrp_garp_master_refresh {garp_master_refresh}', config)
self.assertIn(f'vrrp_garp_master_refresh_repeat {garp_master_refresh_repeat}', config)
+ self.assertIn(f'vrrp_version {vrrp_version}', config)
for group in groups:
vlan_id = group.lstrip('VLAN')
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index 2e09173a7..8867cb427 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -22,8 +22,8 @@ from base_interfaces_test import BasicInterfaceTest
from vyos.ifconfig import Section
from vyos.ifconfig.interface import Interface
from vyos.configsession import ConfigSessionError
-from vyos.util import get_interface_config
-from vyos.util import read_file
+from vyos.utils.network import get_interface_config
+from vyos.utils.file import read_file
class BondingInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
@@ -37,9 +37,8 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
if 'TEST_ETH' in os.environ:
cls._members = os.environ['TEST_ETH'].split()
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._members.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._members.append(tmp)
cls._options = {'bond0' : []}
for member in cls._members:
@@ -243,4 +242,4 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
self.assertIn(member, slaves)
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index 85b45c8fa..cdff49f4b 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -24,10 +24,10 @@ from glob import glob
from netifaces import interfaces
from vyos.ifconfig import Section
-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
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import is_intf_addr_assigned
class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
@@ -41,9 +41,8 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
if 'TEST_ETH' in os.environ:
cls._members = os.environ['TEST_ETH'].split()
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._members.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._members.append(tmp)
cls._options['br0'] = []
for member in cls._members:
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index a1bc8481d..a39b81348 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -28,10 +28,10 @@ from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.pki import CERT_BEGIN
from vyos.template import is_ipv6
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
-from vyos.validate import is_ipv6_link_local
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.network import is_ipv6_link_local
server_ca_root_cert_data = """
MIIBcTCCARagAwIBAgIUDcAf1oIQV+6WRaW7NPcSnECQ/lUwCgYIKoZIzj0EAwIw
@@ -250,10 +250,19 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
for interface in self._interfaces:
# Enable EAPoL
self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-server-ca-intermediate'])
+ self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-client-ca-intermediate'])
self.cli_set(self._base_path + [interface, 'eapol', 'certificate', cert_name])
self.cli_commit()
+ # Test multiple CA chains
+ self.assertEqual(get_certificate_count(interface, 'ca'), 4)
+
+ for interface in self._interfaces:
+ self.cli_delete(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-client-ca-intermediate'])
+
+ self.cli_commit()
+
# Check for running process
self.assertTrue(process_named_running('wpa_supplicant'))
diff --git a/smoketest/scripts/cli/test_interfaces_geneve.py b/smoketest/scripts/cli/test_interfaces_geneve.py
index 24d350aeb..5f8fae91e 100755
--- a/smoketest/scripts/cli/test_interfaces_geneve.py
+++ b/smoketest/scripts/cli/test_interfaces_geneve.py
@@ -17,7 +17,7 @@
import unittest
from vyos.ifconfig import Interface
-from vyos.util import get_interface_config
+from vyos.utils.network import get_interface_config
from base_interfaces_test import BasicInterfaceTest
@@ -43,6 +43,7 @@ class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'df', 'set'])
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'tos', tos])
+ self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'innerproto'])
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'ttl', str(ttl)])
ttl += 10
@@ -67,6 +68,11 @@ class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
label = options['linkinfo']['info_data']['label']
self.assertIn(f'parameters ipv6 flowlabel {label}', self._options[interface])
+ if any('innerproto' in s for s in self._options[interface]):
+ inner = options['linkinfo']['info_data']['innerproto']
+ self.assertIn(f'parameters ip {inner}', self._options[interface])
+
+
self.assertEqual('geneve', options['linkinfo']['info_kind'])
self.assertEqual('set', options['linkinfo']['info_data']['df'])
self.assertEqual(f'0x{tos}', options['linkinfo']['info_data']['tos'])
diff --git a/smoketest/scripts/cli/test_interfaces_input.py b/smoketest/scripts/cli/test_interfaces_input.py
index b4cae4695..3ddf86000 100755
--- a/smoketest/scripts/cli/test_interfaces_input.py
+++ b/smoketest/scripts/cli/test_interfaces_input.py
@@ -16,7 +16,7 @@
import unittest
-from vyos.util import read_file
+from vyos.utils.file import read_file
from vyos.ifconfig import Interface
from base_vyostest_shim import VyOSUnitTestSHIM
diff --git a/smoketest/scripts/cli/test_interfaces_l2tpv3.py b/smoketest/scripts/cli/test_interfaces_l2tpv3.py
index b1d5b7c19..af3d49f75 100755
--- a/smoketest/scripts/cli/test_interfaces_l2tpv3.py
+++ b/smoketest/scripts/cli/test_interfaces_l2tpv3.py
@@ -19,7 +19,7 @@ import json
import unittest
from base_interfaces_test import BasicInterfaceTest
-from vyos.util import cmd
+from vyos.utils.process import cmd
class L2TPv3InterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
diff --git a/smoketest/scripts/cli/test_interfaces_loopback.py b/smoketest/scripts/cli/test_interfaces_loopback.py
index cde90189b..0454dc658 100755
--- a/smoketest/scripts/cli/test_interfaces_loopback.py
+++ b/smoketest/scripts/cli/test_interfaces_loopback.py
@@ -19,7 +19,7 @@ import unittest
from base_interfaces_test import BasicInterfaceTest
from netifaces import interfaces
-from vyos.validate import is_intf_addr_assigned
+from vyos.utils.network import is_intf_addr_assigned
loopbacks = ['127.0.0.1', '::1']
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index 433336f07..ea0f00071 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-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,10 +23,10 @@ from netifaces import interfaces
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import cmd
-from vyos.util import read_file
-from vyos.util import get_interface_config
-from vyos.util import process_named_running
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
+from vyos.utils.network import get_interface_config
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'wpa_supplicant'
@@ -139,15 +139,9 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
self.assertIn(interface, interfaces())
- self.assertIn(interface, interfaces())
- self.assertEqual(cipher, get_cipher(interface))
- # check that we use the new macsec_csindex option (T4537)
- tmp = get_config_value(src_interface, 'macsec_csindex')
- self.assertIn("0", tmp)
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # Verify proper cipher suite (T4537)
+ self.assertEqual(cipher, get_cipher(interface))
def test_macsec_gcm_aes_256(self):
src_interface = 'eth0'
@@ -168,18 +162,12 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
self.assertIn(interface, interfaces())
- self.assertEqual(cipher, get_cipher(interface))
- # check that we use the new macsec_csindex option (T4537)
- tmp = get_config_value(src_interface, 'macsec_csindex')
- self.assertIn("1", tmp)
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # Verify proper cipher suite (T4537)
+ self.assertEqual(cipher, get_cipher(interface))
def test_macsec_source_interface(self):
# Ensure source-interface can bot be part of any other bond or bridge
-
base_bridge = ['interfaces', 'bridge', 'br200']
base_bond = ['interfaces', 'bonding', 'bond200']
@@ -205,9 +193,77 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_commit()
self.assertIn(interface, interfaces())
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ def test_macsec_static_keys(self):
+ src_interface = 'eth0'
+ interface = 'macsec5'
+ cipher1 = 'gcm-aes-128'
+ cipher2 = 'gcm-aes-256'
+ tx_key_1 = '71a82a48eddfa12c08a19792ca20c4bb'
+ tx_key_2 = 'dd487b2958e855ea35a5d43a5ecb3dcfbe7889ffcb877770252feb13b734478d'
+ rx_key_1 = '0022d00f57e75241a230cdf7118dfcc5'
+ rx_key_2 = 'b7d6d7ad075e02323fdeb845217b884d3f93ff36b2cdaf6b07eeb189b877245f'
+ peer_mac = '00:11:22:33:44:55'
+ self.cli_set(self._base_path + [interface])
-if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ # Encrypt link
+ self.cli_set(self._base_path + [interface, 'security', 'encrypt'])
+ # check validate() - source interface is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'source-interface', src_interface])
+
+ # check validate() - cipher is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'security', 'cipher', cipher1])
+
+ # check validate() - only static or mka config is allowed
+ self.cli_set(self._base_path + [interface, 'security', 'static'])
+ self.cli_set(self._base_path + [interface, 'security', 'mka', 'cak', tx_key_1])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(self._base_path + [interface, 'security', 'mka'])
+
+ # check validate() - tx-key required
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # check validate() - tx-key length must match cipher
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_2])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_1])
+
+ # check validate() - at least one peer must be defined
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # check validate() - enabled peer must have both rx-key and MAC defined
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'mac', peer_mac])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'mac'])
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'key', rx_key_1])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'mac', peer_mac])
+
+ # check validate() - peer rx-key length must match cipher
+ self.cli_set(self._base_path + [interface, 'security', 'cipher', cipher2])
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_2])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'key', rx_key_2])
+
+ # final commit and verify
+ self.cli_commit()
+ self.assertIn(interface, interfaces())
+ self.assertEqual(cipher2, get_cipher(interface))
+ self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}'))
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_netns.py b/smoketest/scripts/cli/test_interfaces_netns.py
index dffa7e0ac..b8bebb221 100755
--- a/smoketest/scripts/cli/test_interfaces_netns.py
+++ b/smoketest/scripts/cli/test_interfaces_netns.py
@@ -23,7 +23,7 @@ from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
-from vyos.util import cmd
+from vyos.utils.process import cmd
base_path = ['netns']
namespaces = ['mgmt', 'front', 'back', 'ams-ix']
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index c80c7cf80..d1ece84d6 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -24,9 +24,9 @@ from netifaces import interfaces
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
from vyos.template import address_from_cidr
from vyos.template import dec_ip
from vyos.template import inc_ip
@@ -337,10 +337,6 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
self.cli_delete(path + ['protocol'])
-
- # check validate() - must specify "tls dh-params" when "tls role" is "passive"
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
self.cli_set(path + ['tls', 'dh-params', 'ovpn_test'])
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index 9dce2b52c..2a7a519fd 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -19,7 +19,7 @@ import unittest
from base_interfaces_test import BasicInterfaceTest
from vyos.configsession import ConfigSessionError
-from vyos.util import get_interface_config
+from vyos.utils.network import get_interface_config
from vyos.template import inc_ip
remote_ip4 = '192.0.2.100'
diff --git a/smoketest/scripts/cli/test_interfaces_virtual_ethernet.py b/smoketest/scripts/cli/test_interfaces_virtual_ethernet.py
index 4710930a0..7874589ca 100755
--- a/smoketest/scripts/cli/test_interfaces_virtual_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_virtual_ethernet.py
@@ -19,7 +19,7 @@ import unittest
from netifaces import interfaces
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
from base_interfaces_test import BasicInterfaceTest
class VEthInterfaceTest(BasicInterfaceTest.TestCase):
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index 60a9d1966..f6b203de4 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -18,8 +18,8 @@ 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.utils.network import get_bridge_fdb
+from vyos.utils.network import get_interface_config
from vyos.template import is_ipv6
from base_interfaces_test import BasicInterfaceTest
diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py
index 14fc8d109..48c7cb6a1 100755
--- a/smoketest/scripts/cli/test_interfaces_wireguard.py
+++ b/smoketest/scripts/cli/test_interfaces_wireguard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,6 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.utils.file import read_file
base_path = ['interfaces', 'wireguard']
@@ -35,7 +36,7 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- def test_wireguard_peer(self):
+ def test_01_wireguard_peer(self):
# Create WireGuard interfaces with associated peers
for intf in self._interfaces:
peer = 'foo-' + intf
@@ -62,7 +63,7 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.assertTrue(os.path.isdir(f'/sys/class/net/{intf}'))
- def test_wireguard_add_remove_peer(self):
+ def test_02_wireguard_add_remove_peer(self):
# T2939: Create WireGuard interfaces with associated peers.
# Remove one of the configured peers.
# T4774: Test prevention of duplicate peer public keys
@@ -100,5 +101,56 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path + [interface, 'peer', 'PEER01'])
self.cli_commit()
+ def test_03_wireguard_same_public_key(self):
+ # T5413: Test prevention of equality interface public key and peer's
+ # public key
+ interface = 'wg0'
+ port = '12345'
+ privkey = 'OOjcXGfgQlAuM6q8Z9aAYduCua7pxf7UKYvIqoUPoGQ='
+ pubkey_fail = 'eiVeYKq66mqKLbrZLzlckSP9voaw8jSFyVNiNTdZDjU='
+ pubkey_ok = 'ebFx/1G0ti8tvuZd94sEIosAZZIznX+dBAKG/8DFm0I='
+
+ self.cli_set(base_path + [interface, 'address', '172.16.0.1/24'])
+ self.cli_set(base_path + [interface, 'private-key', privkey])
+
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'public-key', pubkey_fail])
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'port', port])
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'allowed-ips', '10.205.212.10/32'])
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'address', '192.0.2.1'])
+
+ # The same pubkey as the interface wg0
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'public-key', pubkey_ok])
+
+ # Commit peers
+ self.cli_commit()
+
+ self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}'))
+
+ def test_04_wireguard_threaded(self):
+ # T5409: Test adding threaded option on interface.
+ # Test prevention for adding threaded
+ # if no enabled peer is configured.
+ interface = 'wg0'
+ port = '12345'
+ privkey = 'OOjcXGfgQlAuM6q8Z9aAYduCua7pxf7UKYvIqoUPoGQ='
+ pubkey = 'ebFx/1G0ti8tvuZd94sEIosAZZIznX+dBAKG/8DFm0I='
+
+ self.cli_set(base_path + [interface, 'address', '172.16.0.1/24'])
+ self.cli_set(base_path + [interface, 'private-key', privkey])
+
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'port', port])
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'public-key', pubkey])
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'allowed-ips', '10.205.212.10/32'])
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'address', '192.0.2.1'])
+ self.cli_set(base_path + [interface, 'per-client-thread'])
+
+ # Commit peers
+ self.cli_commit()
+ tmp = read_file(f'/sys/class/net/{interface}/threaded')
+ self.assertTrue(tmp, "1")
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index 5d77454b5..95246a7b9 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -22,9 +22,9 @@ from base_interfaces_test import BasicInterfaceTest
from glob import glob
from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
-from vyos.util import check_kmod
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.kernel import check_kmod
+from vyos.utils.file import read_file
def get_config_value(interface, key):
tmp = read_file(f'/run/hostapd/{interface}.conf')
@@ -49,6 +49,10 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
# call base-classes classmethod
super(WirelessInterfaceTest, cls).setUpClass()
+ # T5245 - currently testcases are disabled
+ cls._test_ipv6 = False
+ cls._test_vlan = False
+
def test_wireless_add_single_ip_address(self):
# derived method to check if member interfaces are enslaved properly
super().test_add_single_ip_address()
@@ -93,6 +97,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
vht_opt = {
# VyOS CLI option hostapd - ht_capab setting
+ 'channel-set-width 3' : '[VHT160-80PLUS80]',
'stbc tx' : '[TX-STBC-2BY1]',
'stbc rx 12' : '[RX-STBC-12]',
'ldpc' : '[RXLDPC]',
@@ -100,7 +105,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
'vht-cf' : '[HTC-VHT]',
'antenna-pattern-fixed' : '[RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]',
'max-mpdu 11454' : '[MAX-MPDU-11454]',
- 'max-mpdu-exp 2' : '[MAX-A-MPDU-LEN-EXP-2][VHT160]',
+ 'max-mpdu-exp 2' : '[MAX-A-MPDU-LEN-EXP-2]',
'link-adaptation both' : '[VHT-LINK-ADAPT3]',
'short-gi 80' : '[SHORT-GI-80]',
'short-gi 160' : '[SHORT-GI-160]',
@@ -230,9 +235,51 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
self.assertIn(interface, bridge_members)
self.cli_delete(bridge_path)
- self.cli_delete(self._base_path)
+
+ def test_wireless_security_station_address(self):
+ interface = 'wlan0'
+ ssid = 'VyOS-ACL'
+
+ hostapd_accept_station_conf = f'/run/hostapd/{interface}_station_accept.conf'
+ hostapd_deny_station_conf = f'/run/hostapd/{interface}_station_deny.conf'
+
+ accept_mac = ['00:00:00:00:ac:01', '00:00:00:00:ac:02', '00:00:00:00:ac:03', '00:00:00:00:ac:04']
+ deny_mac = ['00:00:00:00:de:01', '00:00:00:00:de:02', '00:00:00:00:de:03', '00:00:00:00:de:04']
+
+ self.cli_set(self._base_path + [interface, 'ssid', ssid])
+ self.cli_set(self._base_path + [interface, 'country-code', 'se'])
+ self.cli_set(self._base_path + [interface, 'type', 'access-point'])
+ self.cli_set(self._base_path + [interface, 'security', 'station-address', 'mode', 'accept'])
+
+ for mac in accept_mac:
+ self.cli_set(self._base_path + [interface, 'security', 'station-address', 'accept', 'mac', mac])
+ for mac in deny_mac:
+ self.cli_set(self._base_path + [interface, 'security', 'station-address', 'deny', 'mac', mac])
+
self.cli_commit()
+ # in accept mode all addresses are allowed unless specified in the deny list
+ tmp = get_config_value(interface, 'macaddr_acl')
+ self.assertEqual(tmp, '0')
+
+ accept_list = read_file(hostapd_accept_station_conf)
+ for mac in accept_mac:
+ self.assertIn(mac, accept_list)
+
+ deny_list = read_file(hostapd_deny_station_conf)
+ for mac in deny_mac:
+ self.assertIn(mac, deny_list)
+
+ # Switch mode accept -> deny
+ self.cli_set(self._base_path + [interface, 'security', 'station-address', 'mode', 'deny'])
+ self.cli_commit()
+ # In deny mode all addresses are denied unless specified in the allow list
+ tmp = get_config_value(interface, 'macaddr_acl')
+ self.assertEqual(tmp, '1')
+
+ # Check for running process
+ self.assertTrue(process_named_running('hostapd'))
+
if __name__ == '__main__':
check_kmod('mac80211_hwsim')
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py b/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
index 23a681321..a33fd5c18 100755
--- a/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
+++ b/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
@@ -19,8 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'haproxy'
HAPROXY_CONF = '/run/haproxy/haproxy.cfg'
diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py
index 8df3471f7..9b2cb0fac 100755
--- a/smoketest/scripts/cli/test_load_balancing_wan.py
+++ b/smoketest/scripts/cli/test_load_balancing_wan.py
@@ -21,8 +21,8 @@ import time
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import call
-from vyos.util import cmd
+from vyos.utils.process import call
+from vyos.utils.process import cmd
base_path = ['load-balancing']
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 1f2b777a8..31dfcef87 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -21,8 +21,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
base_path = ['nat']
src_path = base_path + ['source']
@@ -231,5 +231,69 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_static_nat')
+ def test_dnat_redirect(self):
+ dst_addr_1 = '10.0.1.1'
+ dest_port = '5122'
+ protocol = 'tcp'
+ redirected_port = '22'
+ ifname = 'eth0'
+
+ self.cli_set(dst_path + ['rule', '10', 'destination', 'address', dst_addr_1])
+ self.cli_set(dst_path + ['rule', '10', 'destination', 'port', dest_port])
+ self.cli_set(dst_path + ['rule', '10', 'protocol', protocol])
+ self.cli_set(dst_path + ['rule', '10', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '10', 'translation', 'redirect', 'port', redirected_port])
+
+ self.cli_set(dst_path + ['rule', '20', 'destination', 'address', dst_addr_1])
+ self.cli_set(dst_path + ['rule', '20', 'destination', 'port', dest_port])
+ self.cli_set(dst_path + ['rule', '20', 'protocol', protocol])
+ self.cli_set(dst_path + ['rule', '20', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '20', 'translation', 'redirect'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'iifname "{ifname}"', f'ip daddr {dst_addr_1}', f'{protocol} dport {dest_port}', f'redirect to :{redirected_port}'],
+ [f'iifname "{ifname}"', f'ip daddr {dst_addr_1}', f'{protocol} dport {dest_port}', f'redirect']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
+
+ def test_nat_balance(self):
+ ifname = 'eth0'
+ member_1 = '198.51.100.1'
+ weight_1 = '10'
+ member_2 = '198.51.100.2'
+ weight_2 = '90'
+ member_3 = '192.0.2.1'
+ weight_3 = '35'
+ member_4 = '192.0.2.2'
+ weight_4 = '65'
+ dst_port = '443'
+
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
+ self.cli_set(dst_path + ['rule', '1', 'destination', 'port', dst_port])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'source-address'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'source-port'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'destination-address'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'destination-port'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'backend', member_1, 'weight', weight_1])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'backend', member_2, 'weight', weight_2])
+
+ self.cli_set(src_path + ['rule', '1', 'outbound-interface', ifname])
+ self.cli_set(src_path + ['rule', '1', 'load-balance', 'hash', 'random'])
+ self.cli_set(src_path + ['rule', '1', 'load-balance', 'backend', member_3, 'weight', weight_3])
+ self.cli_set(src_path + ['rule', '1', 'load-balance', 'backend', member_4, 'weight', weight_4])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'iifname "{ifname}"', f'tcp dport {dst_port}', f'dnat to jhash ip saddr . tcp sport . ip daddr . tcp dport mod 100 map', f'0-9 : {member_1}, 10-99 : {member_2}'],
+ [f'oifname "{ifname}"', f'snat to numgen random mod 100 map', f'0-34 : {member_3}, 35-99 : {member_4}']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index 50806b3e8..e062f28a6 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -22,8 +22,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
base_path = ['nat66']
src_path = base_path + ['source']
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index f35cdaa4c..354f791bd 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
+from vyos.utils.process import cmd
base_path = ['policy']
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index a3df6bf4d..d9b64544a 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,7 +18,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import cmd
+from vyos.utils.process import cmd
mark = '100'
conn_mark = '555'
@@ -100,7 +100,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
nftables_search = [
- [f'iifname "{interface}"','jump VYOS_PBR_smoketest'],
+ [f'iifname "{interface}"','jump VYOS_PBR_UD_smoketest'],
['ip daddr @N_smoketest_network1', 'ip saddr @N_smoketest_network'],
]
@@ -119,7 +119,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
mark_hex = "{0:#010x}".format(int(mark))
nftables_search = [
- [f'iifname "{interface}"','jump VYOS_PBR_smoketest'],
+ [f'iifname "{interface}"','jump VYOS_PBR_UD_smoketest'],
['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'meta mark set ' + mark_hex],
]
@@ -138,7 +138,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
mark_hex_set = "{0:#010x}".format(int(conn_mark_set))
nftables_search = [
- [f'iifname "{interface}"','jump VYOS_PBR_smoketest'],
+ [f'iifname "{interface}"','jump VYOS_PBR_UD_smoketest'],
['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'ct mark ' + mark_hex, 'ct mark set ' + mark_hex_set],
]
@@ -164,7 +164,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
# IPv4
nftables_search = [
- [f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'],
+ [f'iifname "{interface}"', 'jump VYOS_PBR_UD_smoketest'],
['tcp flags syn / syn,ack', 'tcp dport 8888', 'meta mark set ' + mark_hex]
]
@@ -173,7 +173,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
# IPv6
nftables6_search = [
- [f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'],
+ [f'iifname "{interface}"', 'jump VYOS_PBR6_UD_smoketest'],
['meta l4proto { tcp, udp }', 'th dport 8888', 'meta mark set ' + mark_hex]
]
@@ -246,7 +246,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
# IPv4
nftables_search = [
- ['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_smoketest'],
+ ['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_UD_smoketest'],
['meta l4proto udp', 'drop'],
['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex],
['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark set ' + mark_hex],
@@ -258,7 +258,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
# IPv6
nftables6_search = [
- [f'iifname "{interface_wc}"', 'jump VYOS_PBR6_smoketest'],
+ [f'iifname "{interface_wc}"', 'jump VYOS_PBR6_UD_smoketest'],
['meta l4proto udp', 'drop'],
['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex],
['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex],
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index fdc254a05..451565664 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -18,7 +18,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'bfdd'
base_path = ['protocols', 'bfd']
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 2fd5d0c9b..77952d8d9 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-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,9 +18,10 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.ifconfig import Section
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv6
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'bgpd'
ASN = '64512'
@@ -164,7 +165,6 @@ peer_group_config = {
'local_role_strict': '',
},
}
-
class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -201,12 +201,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
-
def create_bgp_instances_for_import_test(self):
table = '1000'
- self.cli_set(base_path + ['system-as', ASN])
- # testing only one AFI is sufficient as it's generic code
-
self.cli_set(import_vrf_base + [import_vrf, 'table', table])
self.cli_set(import_vrf_base + [import_vrf, 'protocols', 'bgp', 'system-as', ASN])
@@ -286,7 +282,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'disable_conn_chk' in peer_config:
self.assertIn(f' neighbor {peer} disable-connected-check', frrconfig)
-
def test_bgp_01_simple(self):
router_id = '127.0.0.1'
local_pref = '500'
@@ -374,7 +369,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' maximum-paths {max_path_v6}', afiv6_config)
self.assertIn(f' maximum-paths ibgp {max_path_v6ibgp}', afiv6_config)
-
def test_bgp_02_neighbors(self):
# Test out individual neighbor configuration items, not all of them are
# also available to a peer-group!
@@ -569,7 +563,6 @@ 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()
@@ -585,7 +578,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'peer_group' in peer_config:
self.assertIn(f' neighbor {peer} peer-group {peer_config["peer_group"]}', frrconfig)
-
def test_bgp_04_afi_ipv4(self):
networks = {
'10.0.0.0/8' : {
@@ -633,7 +625,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'summary_only' in network_config:
self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
-
def test_bgp_05_afi_ipv6(self):
networks = {
'2001:db8:100::/48' : {
@@ -680,7 +671,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'as_set' in network_config:
self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
-
def test_bgp_06_listen_range(self):
# Implemented via T1875
limit = '64'
@@ -791,7 +781,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'{family}', frrconfig)
self.assertIn(f'local-install {flowspec_int}', frrconfig)
-
def test_bgp_10_vrf_simple(self):
router_id = '127.0.0.3'
vrfs = ['red', 'green', 'blue']
@@ -801,9 +790,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
# templates and Jinja2 FRR template.
table = '1000'
- self.cli_set(base_path + ['system-as', ASN])
# testing only one AFI is sufficient as it's generic code
-
for vrf in vrfs:
vrf_base = ['vrf', 'name', vrf]
self.cli_set(vrf_base + ['table', table])
@@ -834,7 +821,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
confed_id = str(int(ASN) + 1)
confed_asns = '10 20 30 40'
- self.cli_set(base_path + ['system-as', ASN])
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'confederation', 'identifier', confed_id])
for asn in confed_asns.split():
@@ -850,12 +836,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' bgp confederation identifier {confed_id}', frrconfig)
self.assertIn(f' bgp confederation peers {confed_asns}', frrconfig)
-
def test_bgp_12_v6_link_local(self):
remote_asn = str(int(ASN) + 10)
interface = 'eth0'
- self.cli_set(base_path + ['system-as', ASN])
self.cli_set(base_path + ['neighbor', interface, 'address-family', 'ipv6-unicast'])
self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as', remote_asn])
@@ -870,7 +854,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {interface} activate', frrconfig)
self.assertIn(f' exit-address-family', frrconfig)
-
def test_bgp_13_vpn(self):
remote_asn = str(int(ASN) + 150)
neighbor = '192.0.2.55'
@@ -880,12 +863,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
rt_export = f'{neighbor}:1002 1.2.3.4:567'
rt_import = f'{neighbor}:1003 500:100'
- self.cli_set(base_path + ['system-as', ASN])
# testing only one AFI is sufficient as it's generic code
for afi in ['ipv4-unicast', 'ipv6-unicast']:
self.cli_set(base_path + ['address-family', afi, 'export', 'vpn'])
self.cli_set(base_path + ['address-family', afi, 'import', 'vpn'])
self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label])
+ self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'allocation-mode', 'per-nexthop'])
self.cli_set(base_path + ['address-family', afi, 'rd', 'vpn', 'export', rd])
self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'export', route_map_out])
self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'import', route_map_in])
@@ -905,6 +888,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' export vpn', afi_config)
self.assertIn(f' import vpn', afi_config)
self.assertIn(f' label vpn export {label}', afi_config)
+ self.assertIn(f' label vpn export allocation-mode per-nexthop', afi_config)
self.assertIn(f' rd vpn export {rd}', afi_config)
self.assertIn(f' route-map vpn export {route_map_out}', afi_config)
self.assertIn(f' route-map vpn import {route_map_in}', afi_config)
@@ -919,7 +903,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
peer_group = 'bar'
interface = 'eth0'
- self.cli_set(base_path + ['system-as', ASN])
self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn])
self.cli_set(base_path + ['neighbor', neighbor, 'peer-group', peer_group])
self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', remote_asn])
@@ -959,7 +942,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
remote_asn = '500'
local_asn = '400'
- self.cli_set(base_path + ['system-as', ASN])
self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', ASN])
self.cli_set(base_path + ['neighbor', neighbor, 'local-as', local_asn])
@@ -1072,5 +1054,32 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
+ def test_bgp_22_interface_mpls_forwarding(self):
+ interfaces = Section.interfaces('ethernet', vlan=False)
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'mpls', 'forwarding'])
+
+ self.cli_commit()
+
+ for interface in interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}')
+ self.assertIn(f'interface {interface}', frrconfig)
+ self.assertIn(f' mpls bgp forwarding', frrconfig)
+
+ def test_bgp_23_vrf_interface_mpls_forwarding(self):
+ self.create_bgp_instances_for_import_test()
+ interfaces = Section.interfaces('ethernet', vlan=False)
+ for interface in interfaces:
+ self.cli_set(['interfaces', 'ethernet', interface, 'vrf', import_vrf])
+ self.cli_set(import_vrf_base + [import_vrf] + base_path + ['interface', interface, 'mpls', 'forwarding'])
+
+ self.cli_commit()
+
+ for interface in interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}')
+ self.assertIn(f'interface {interface}', frrconfig)
+ self.assertIn(f' mpls bgp forwarding', frrconfig)
+ self.cli_delete(['interfaces', 'ethernet', interface, 'vrf'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_igmp-proxy.py b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
index 079b5bee5..a75003b12 100755
--- a/smoketest/scripts/cli/test_protocols_igmp-proxy.py
+++ b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
@@ -19,8 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import read_file
-from vyos.util import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'igmpproxy'
IGMP_PROXY_CONF = '/etc/igmpproxy.conf'
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index f1a030e77..5ab7fae14 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'isisd'
base_path = ['protocols', 'isis']
@@ -295,9 +295,10 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown])
- # Commit interface changes for holddown
- self.cli_commit()
+ # Commit interface changes for holddown
+ self.cli_commit()
+ for interface in self._interfaces:
# Verify interface changes for holddown
tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
self.assertIn(f'interface {interface}', tmp)
@@ -308,9 +309,10 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable'])
- # Commit interface changes for disable
- self.cli_commit()
+ # Commit interface changes for disable
+ self.cli_commit()
+ for interface in self._interfaces:
# Verify interface changes for disable
tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
self.assertIn(f'interface {interface}', tmp)
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
index 76e6ca35a..06f21c6e1 100755
--- a/smoketest/scripts/cli/test_protocols_mpls.py
+++ b/smoketest/scripts/cli/test_protocols_mpls.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'ldpd'
base_path = ['protocols', 'mpls', 'ldp']
diff --git a/smoketest/scripts/cli/test_protocols_nhrp.py b/smoketest/scripts/cli/test_protocols_nhrp.py
index 7dbe836f7..45ef539f6 100755
--- a/smoketest/scripts/cli/test_protocols_nhrp.py
+++ b/smoketest/scripts/cli/test_protocols_nhrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,7 +19,9 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.firewall import find_nftables_rule
-from vyos.util import call, process_named_running, read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.process import call
tunnel_path = ['interfaces', 'tunnel']
nhrp_path = ['protocols', 'nhrp']
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index 6fe6dd979..a6850db71 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -20,7 +20,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'ospfd'
base_path = ['protocols', 'ospf']
@@ -56,7 +56,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
@@ -84,7 +84,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' compatible rfc1583', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth {bandwidth}', frrconfig)
@@ -116,7 +116,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
for ptotocol in protocols:
@@ -137,7 +137,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -147,7 +147,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -159,6 +159,12 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
on_startup = '30'
on_shutdown = '60'
refresh = '50'
+ aggregation_timer = '100'
+ summary_nets = {
+ '10.0.1.0/24' : {},
+ '10.0.2.0/24' : {'tag' : '50'},
+ '10.0.3.0/24' : {'no_advertise' : {}},
+ }
self.cli_set(base_path + ['distance', 'global', global_distance])
self.cli_set(base_path + ['distance', 'ospf', 'external', external])
@@ -170,11 +176,20 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['mpls-te', 'enable'])
self.cli_set(base_path + ['refresh', 'timers', refresh])
+ self.cli_set(base_path + ['aggregation', 'timer', aggregation_timer])
+
+ for summary, summary_options in summary_nets.items():
+ self.cli_set(base_path + ['summary-address', summary])
+ if 'tag' in summary_options:
+ self.cli_set(base_path + ['summary-address', summary, 'tag', summary_options['tag']])
+ if 'no_advertise' in summary_options:
+ self.cli_set(base_path + ['summary-address', summary, 'no-advertise'])
+
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' mpls-te on', frrconfig)
self.assertIn(f' mpls-te router-address 0.0.0.0', frrconfig) # default
@@ -184,12 +199,20 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' max-metric router-lsa on-shutdown {on_shutdown}', frrconfig)
self.assertIn(f' refresh timer {refresh}', frrconfig)
+ self.assertIn(f' aggregation timer {aggregation_timer}', frrconfig)
+ for summary, summary_options in summary_nets.items():
+ self.assertIn(f' summary-address {summary}', frrconfig)
+ if 'tag' in summary_options:
+ tag = summary_options['tag']
+ self.assertIn(f' summary-address {summary} tag {tag}', frrconfig)
+ if 'no_advertise' in summary_options:
+ self.assertIn(f' summary-address {summary} no-advertise', frrconfig)
# enable inter-area
self.cli_set(base_path + ['distance', 'ospf', 'inter-area', inter_area])
self.cli_commit()
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f' distance ospf intra-area {intra_area} inter-area {inter_area} external {external}', frrconfig)
@@ -205,7 +228,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
for neighbor in neighbors:
self.assertIn(f' neighbor {neighbor} priority {priority} poll-interval {poll_interval}', frrconfig) # default
@@ -224,7 +247,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -251,7 +274,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' area {area} shortcut {shortcut}', frrconfig)
self.assertIn(f' area {area} virtual-link {virtual_link} hello-interval {hello} retransmit-interval {retransmit} transmit-delay {transmit} dead-interval {dead}', frrconfig)
@@ -283,11 +306,12 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' passive-interface default', frrconfig)
for interface in interfaces:
+ # Can not use daemon for getFRRconfig() as bandwidth parameter belongs to zebra process
config = self.getFRRconfig(f'interface {interface}')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf authentication-key {password}', config)
@@ -300,6 +324,17 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' no ip ospf passive', config)
self.assertIn(f' bandwidth {bandwidth}', config)
+ # T5467: Remove interface from OSPF process and VRF
+ self.cli_delete(base_path + ['interface'])
+ self.cli_commit()
+
+ for interface in interfaces:
+ # T5467: It must also be removed from FRR config
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertNotIn(f'interface {interface}', frrconfig)
+ # There should be no OSPF related command at all under the interface
+ self.assertNotIn(f' ip ospf', frrconfig)
+
def test_ospf_11_interface_area(self):
area = '0'
interfaces = Section.interfaces('ethernet')
@@ -316,11 +351,11 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
for interface in interfaces:
- config = self.getFRRconfig(f'interface {interface}')
+ config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf area {area}', config)
@@ -332,8 +367,10 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
vrf = 'blue'
vrf_base = ['vrf', 'name', vrf]
vrf_iface = 'eth1'
+ area = '1'
+
self.cli_set(vrf_base + ['table', table])
- self.cli_set(vrf_base + ['protocols', 'ospf', 'interface', vrf_iface])
+ self.cli_set(vrf_base + ['protocols', 'ospf', 'interface', vrf_iface, 'area', area])
self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
# Also set a default VRF OSPF config
@@ -341,16 +378,31 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
- frrconfig = self.getFRRconfig(f'router ospf vrf {vrf}')
+ frrconfig = self.getFRRconfig(f'router ospf vrf {vrf}', daemon=PROCESS_NAME)
self.assertIn(f'router ospf vrf {vrf}', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {vrf_iface}', frrconfig)
+ self.assertIn(f' ip ospf area {area}', frrconfig)
+
+ # T5467: Remove interface from OSPF process and VRF
+ self.cli_delete(vrf_base + ['protocols', 'ospf', 'interface'])
+ self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
+ self.cli_commit()
+
+ # T5467: It must also be removed from FRR config
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon=PROCESS_NAME)
+ self.assertNotIn(f'interface {vrf_iface}', frrconfig)
+ # There should be no OSPF related command at all under the interface
+ self.assertNotIn(f' ip ospf', frrconfig)
+
# cleanup
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
@@ -362,7 +414,6 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
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'])
@@ -373,7 +424,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
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)
@@ -407,7 +458,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f' segment-routing on', frrconfig)
self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', frrconfig)
self.assertIn(f' segment-routing node-msd {maximum_stack_size}', frrconfig)
@@ -426,7 +477,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify main OSPF changes
- frrconfig = self.getFRRconfig('router ospf')
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig)
self.assertIn(f' mpls ldp-sync holddown {holddown}', frrconfig)
@@ -434,11 +485,12 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown])
- # Commit interface changes for holddown
- self.cli_commit()
+ # Commit interface changes for holddown
+ self.cli_commit()
+ for interface in interfaces:
# Verify interface changes for holddown
- config = self.getFRRconfig(f'interface {interface}')
+ config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf dead-interval 40', config)
self.assertIn(f' ip ospf mpls ldp-sync', config)
@@ -447,14 +499,42 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable'])
- # Commit interface changes for disable
- self.cli_commit()
+ # Commit interface changes for disable
+ self.cli_commit()
+ for interface in interfaces:
# Verify interface changes for disable
- config = self.getFRRconfig(f'interface {interface}')
+ config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf dead-interval 40', config)
self.assertIn(f' no ip ospf mpls ldp-sync', config)
+ def test_ospf_16_graceful_restart(self):
+ period = '300'
+ supported_grace_time = '400'
+ router_ids = ['192.0.2.1', '192.0.2.2']
+
+ self.cli_set(base_path + ['capability', 'opaque'])
+ self.cli_set(base_path + ['graceful-restart', 'grace-period', period])
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'planned-only'])
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'no-strict-lsa-checking'])
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'supported-grace-time', supported_grace_time])
+ for router_id in router_ids:
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'enable', 'router-id', router_id])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME)
+ self.assertIn(f'router ospf', frrconfig)
+ self.assertIn(f' capability opaque', frrconfig)
+ self.assertIn(f' graceful-restart grace-period {period}', frrconfig)
+ self.assertIn(f' graceful-restart helper planned-only', frrconfig)
+ self.assertIn(f' no graceful-restart helper strict-lsa-checking', frrconfig)
+ self.assertIn(f' graceful-restart helper supported-grace-time {supported_grace_time}', frrconfig)
+ for router_id in router_ids:
+ self.assertIn(f' graceful-restart helper enable {router_id}', frrconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index fa80ad555..0d6c6c691 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.py
@@ -20,7 +20,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'ospf6d'
base_path = ['protocols', 'ospfv3']
@@ -74,7 +74,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {default_area} range {prefix}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
@@ -82,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}', daemon='ospf6d')
+ if_config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
self.assertIn(f'ipv6 ospf6 area {default_area}', if_config)
self.cli_delete(['policy', 'access-list6', acl_name])
@@ -103,7 +103,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
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)
@@ -123,7 +123,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
self.assertIn(f'router ospf6', frrconfig)
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig)
@@ -154,13 +154,13 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
self.assertIn(f'router ospf6', frrconfig)
cost = '100'
priority = '10'
for interface in interfaces:
- if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d')
+ if_config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
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)
@@ -172,6 +172,15 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
cost = str(int(cost) + 10)
priority = str(int(priority) + 5)
+ # Cleanup interfaces
+ self.cli_delete(base_path + ['interface'])
+ self.cli_commit()
+
+ for interface in interfaces:
+ if_config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
+ # There should be no OSPF6 configuration at all after interface removal
+ self.assertNotIn(f' ipv6 ospf6', if_config)
+
def test_ospfv3_05_area_stub(self):
area_stub = '23'
@@ -184,7 +193,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
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)
@@ -210,7 +219,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
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)
@@ -230,7 +239,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -239,7 +248,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -265,21 +274,58 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
- frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon='ospf6d')
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon=PROCESS_NAME)
self.assertIn(f'interface {vrf_iface}', frrconfig)
self.assertIn(f' ipv6 ospf6 bfd', frrconfig)
- frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}', daemon='ospf6d')
+ frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}', daemon=PROCESS_NAME)
self.assertIn(f'router ospf6 vrf {vrf}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id_vrf}', frrconfig)
+ # T5467: Remove interface from OSPF process and VRF
+ self.cli_delete(vrf_base + ['protocols', 'ospfv3', 'interface'])
+ self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
+ self.cli_commit()
+
+ # T5467: It must also be removed from FRR config
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon=PROCESS_NAME)
+ self.assertNotIn(f'interface {vrf_iface}', frrconfig)
+ # There should be no OSPF related command at all under the interface
+ self.assertNotIn(f' ipv6 ospf6', frrconfig)
+
# cleanup
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
+
+ def test_ospfv3_09_graceful_restart(self):
+ period = '300'
+ supported_grace_time = '400'
+ router_ids = ['192.0.2.1', '192.0.2.2']
+
+ self.cli_set(base_path + ['graceful-restart', 'grace-period', period])
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'planned-only'])
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'lsa-check-disable'])
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'supported-grace-time', supported_grace_time])
+ for router_id in router_ids:
+ self.cli_set(base_path + ['graceful-restart', 'helper', 'enable', 'router-id', router_id])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6', daemon=PROCESS_NAME)
+ self.assertIn(f'router ospf6', frrconfig)
+ self.assertIn(f' graceful-restart grace-period {period}', frrconfig)
+ self.assertIn(f' graceful-restart helper planned-only', frrconfig)
+ self.assertIn(f' graceful-restart helper lsa-check-disable', frrconfig)
+ self.assertIn(f' graceful-restart helper supported-grace-time {supported_grace_time}', frrconfig)
+ for router_id in router_ids:
+ self.assertIn(f' graceful-restart helper enable {router_id}', frrconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py
index 11385adb5..925499fc8 100755
--- a/smoketest/scripts/cli/test_protocols_rip.py
+++ b/smoketest/scripts/cli/test_protocols_rip.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'ripd'
acl_in = '198'
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index 53336a533..0a8ce7eef 100755
--- a/smoketest/scripts/cli/test_protocols_ripng.py
+++ b/smoketest/scripts/cli/test_protocols_ripng.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.ifconfig import Section
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'ripngd'
acl_in = '198'
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index e5e45565b..f4aedcbc3 100755
--- a/smoketest/scripts/cli/test_protocols_rpki.py
+++ b/smoketest/scripts/cli/test_protocols_rpki.py
@@ -20,8 +20,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import process_named_running
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
base_path = ['protocols', 'rpki']
PROCESS_NAME = 'bgpd'
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index 275f1a1df..abf1080ab 100755
--- a/smoketest/scripts/cli/test_protocols_static.py
+++ b/smoketest/scripts/cli/test_protocols_static.py
@@ -20,7 +20,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv6
-from vyos.util import get_interface_config
+from vyos.utils.network import get_interface_config
base_path = ['protocols', 'static']
vrf_path = ['protocols', 'vrf']
diff --git a/smoketest/scripts/cli/test_protocols_static_arp.py b/smoketest/scripts/cli/test_protocols_static_arp.py
index b61d8f854..7f8047249 100755
--- a/smoketest/scripts/cli/test_protocols_static_arp.py
+++ b/smoketest/scripts/cli/test_protocols_static_arp.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import cmd
+from vyos.utils.process import cmd
base_path = ['protocols', 'static', 'arp']
interface = 'eth0'
diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py
index 0092473d6..3743be788 100755
--- a/smoketest/scripts/cli/test_qos.py
+++ b/smoketest/scripts/cli/test_qos.py
@@ -22,7 +22,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import cmd
+from vyos.utils.process import cmd
base_path = ['qos']
@@ -544,4 +544,4 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):
self.assertEqual(f'{dport:x}', filter['options']['match']['value'])
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py
index 92f87c06c..59c4b59a9 100755
--- a/smoketest/scripts/cli/test_service_dhcp-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcp-relay.py
@@ -20,8 +20,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'dhcrelay'
RELAY_CONF = '/run/dhcp-relay/dhcrelay.conf'
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index 64ba100a2..093e43494 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -19,8 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
from vyos.template import address_from_cidr
from vyos.template import inc_ip
from vyos.template import dec_ip
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-relay.py b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
index 8bb58d296..4487f4b0f 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
@@ -21,8 +21,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.template import address_from_cidr
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'dhcrelay'
RELAY_CONF = '/run/dhcp-relay/dhcrelay6.conf'
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py
index f83453323..4d9dabc3f 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-server.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-server.py
@@ -20,8 +20,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.template import inc_ip
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'dhcpd'
DHCPD_CONF = '/run/dhcp-server/dhcpdv6.conf'
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index a3aa41f94..ee8a07b37 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,158 +14,179 @@
# 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
+import tempfile
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_running
-PROCESS_NAME = 'ddclient'
DDCLIENT_CONF = '/run/ddclient/ddclient.conf'
+DDCLIENT_PID = '/run/ddclient/ddclient.pid'
base_path = ['service', 'dns', 'dynamic']
hostname = 'test.ddns.vyos.io'
+zone = 'vyos.io'
+password = 'paSS_@4ord'
interface = 'eth0'
-def get_config_value(key):
- tmp = cmd(f'sudo cat {DDCLIENT_CONF}')
- tmp = re.findall(r'\n?{}=+(.*)'.format(key), tmp)
- tmp = tmp[0].rstrip(',')
- return tmp
-
class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_running(DDCLIENT_PID))
+
# Delete DDNS configuration
self.cli_delete(base_path)
self.cli_commit()
- def test_dyndns_service(self):
- from itertools import product
- ddns = ['interface', interface, 'service']
- users = [None, 'vyos_user']
- services = ['cloudflare', 'afraid', 'dyndns', 'zoneedit']
+ # PID file must no londer exist after process exited
+ self.assertFalse(os.path.exists(DDCLIENT_PID))
- for user, service in product(users, services):
- password = 'vyos_pass'
- zone = 'vyos.io'
+ # IPv4 standard DDNS service configuration
+ def test_01_dyndns_service_standard(self):
+ ddns = ['address', interface, 'service']
+ services = {'cloudflare': {'protocol': 'cloudflare'},
+ 'freedns': {'protocol': 'freedns', 'username': 'vyos_user'},
+ 'zoneedit': {'protocol': 'zoneedit1', 'username': 'vyos_user'}}
+
+ for svc, details in services.items():
+ # Always start with a clean CLI instance
self.cli_delete(base_path)
- self.cli_set(base_path + ddns + [service, 'host-name', hostname])
- if user is not None:
- self.cli_set(base_path + ddns + [service, 'login', user])
- self.cli_set(base_path + ddns + [service, 'password', password])
- self.cli_set(base_path + ddns + [service, 'zone', zone])
+
+ self.cli_set(base_path + ddns + [svc, 'host-name', hostname])
+ self.cli_set(base_path + ddns + [svc, 'password', password])
+ self.cli_set(base_path + ddns + [svc, 'zone', zone])
+ for opt, value in details.items():
+ self.cli_set(base_path + ddns + [svc, opt, value])
# commit changes
- if service == 'cloudflare':
- self.cli_commit()
- elif user is None:
- # not set user is only allowed for cloudflare
- with self.assertRaises(ConfigSessionError):
- # remove zone to test not set user
- self.cli_delete(base_path + ddns + [service, 'zone', 'vyos.io'])
- self.cli_commit()
- # this case is fininshed, user not set is not allowed when service isn't cloudflare
- continue
+ if details['protocol'] == 'cloudflare':
+ pass
else:
- # zone option only works on cloudflare, an exception is raised
- # for all others
+ # zone option does not work on all protocols, an exception is
+ # raised for all others
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_delete(base_path + ddns + [service, 'zone', 'vyos.io'])
- # commit changes again - now it should work
- self.cli_commit()
-
- # we can only read the configuration file when we operate as 'root'
- protocol = get_config_value('protocol')
- login = None if user is None else get_config_value('login')
- pwd = get_config_value('password')
-
- # some services need special treatment
- protoname = service
- if service == 'cloudflare':
- tmp = get_config_value('zone')
- self.assertTrue(tmp == zone)
- elif service == 'afraid':
- protoname = 'freedns'
- elif service == 'dyndns':
- protoname = 'dyndns2'
- elif service == 'zoneedit':
- protoname = 'zoneedit1'
-
- self.assertTrue(protocol == protoname)
- self.assertTrue(login == user)
- self.assertTrue(pwd == "'" + password + "'")
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- def test_dyndns_rfc2136(self):
- # Check if DDNS service can be configured and runs
- ddns = ['interface', interface, 'rfc2136', 'vyos']
- ddns_key_file = '/config/auth/my.key'
-
- self.cli_set(base_path + ddns + ['key', ddns_key_file])
- self.cli_set(base_path + ddns + ['record', 'test.ddns.vyos.io'])
- self.cli_set(base_path + ddns + ['server', 'ns1.vyos.io'])
- self.cli_set(base_path + ddns + ['ttl', '300'])
- self.cli_set(base_path + ddns + ['zone', 'vyos.io'])
+ self.cli_delete(base_path + ddns + [svc, 'zone', zone])
- # ensure an exception will be raised as no key is present
- if os.path.exists(ddns_key_file):
- os.unlink(ddns_key_file)
-
- # check validate() - the key file does not exist yet
- with self.assertRaises(ConfigSessionError):
+ # commit changes
self.cli_commit()
- with open(ddns_key_file, 'w') as f:
- f.write('S3cretKey')
-
- # commit changes
- self.cli_commit()
-
- # TODO: inspect generated configuration file
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- def test_dyndns_ipv6(self):
- ddns = ['interface', interface, 'service', 'dynv6']
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ # default value 300 seconds
+ self.assertIn(f'daemon=300', ddclient_conf)
+ self.assertIn(f'use=if', ddclient_conf)
+ self.assertIn(f'if={interface}', ddclient_conf)
+ self.assertIn(f'password={password}', ddclient_conf)
+
+ for opt in details.keys():
+ if opt == 'username':
+ login = details[opt]
+ self.assertIn(f'login={login}', ddclient_conf)
+ else:
+ tmp = details[opt]
+ self.assertIn(f'{opt}={tmp}', ddclient_conf)
+
+ # IPv6 only DDNS service configuration
+ def test_02_dyndns_service_ipv6(self):
+ timeout = '60'
+ ddns = ['address', interface, 'service', 'dynv6']
proto = 'dyndns2'
user = 'none'
password = 'paSS_4ord'
srv = 'ddns.vyos.io'
+ ip_version = 'ipv6'
- self.cli_set(base_path + ['interface', interface, 'ipv6-enable'])
- self.cli_set(base_path + ddns + ['host-name', hostname])
- self.cli_set(base_path + ddns + ['login', user])
- self.cli_set(base_path + ddns + ['password', password])
+ self.cli_set(base_path + ['timeout', timeout])
+ self.cli_set(base_path + ddns + ['ip-version', ip_version])
self.cli_set(base_path + ddns + ['protocol', proto])
self.cli_set(base_path + ddns + ['server', srv])
+ self.cli_set(base_path + ddns + ['username', user])
+ self.cli_set(base_path + ddns + ['password', password])
+ self.cli_set(base_path + ddns + ['host-name', hostname])
# commit changes
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- protocol = get_config_value('protocol')
- login = get_config_value('login')
- pwd = get_config_value('password')
- server = get_config_value('server')
- usev6 = get_config_value('usev6')
-
- # Check some generating config parameters
- self.assertEqual(protocol, proto)
- self.assertEqual(login, user)
- self.assertEqual(pwd, f"'{password}'")
- self.assertEqual(server, srv)
- self.assertEqual(usev6, f"ifv6, if={interface}")
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ self.assertIn(f'daemon={timeout}', ddclient_conf)
+ self.assertIn(f'usev6=ifv6', ddclient_conf)
+ self.assertIn(f'ifv6={interface}', ddclient_conf)
+ self.assertIn(f'protocol={proto}', ddclient_conf)
+ self.assertIn(f'server={srv}', ddclient_conf)
+ self.assertIn(f'login={user}', ddclient_conf)
+ self.assertIn(f'password={password}', ddclient_conf)
+
+ # IPv4+IPv6 dual DDNS service configuration
+ def test_03_dyndns_service_dual_stack(self):
+ ddns = ['address', interface, 'service']
+ services = {'cloudflare': {'protocol': 'cloudflare', 'zone': 'vyos.io'},
+ 'freedns': {'protocol': 'freedns', 'username': 'vyos_user'}}
+ password = 'vyos_pass'
+ ip_version = 'both'
+
+ for svc, details in services.items():
+ # Always start with a clean CLI instance
+ self.cli_delete(base_path)
+
+ self.cli_set(base_path + ddns + [svc, 'host-name', hostname])
+ self.cli_set(base_path + ddns + [svc, 'password', password])
+ self.cli_set(base_path + ddns + [svc, 'ip-version', ip_version])
+ for opt, value in details.items():
+ self.cli_set(base_path + ddns + [svc, opt, value])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ self.assertIn(f'usev4=ifv4', ddclient_conf)
+ self.assertIn(f'usev6=ifv6', ddclient_conf)
+ self.assertIn(f'ifv4={interface}', ddclient_conf)
+ self.assertIn(f'ifv6={interface}', ddclient_conf)
+ self.assertIn(f'password={password}', ddclient_conf)
+
+ for opt in details.keys():
+ if opt == 'username':
+ login = details[opt]
+ self.assertIn(f'login={login}', ddclient_conf)
+ else:
+ tmp = details[opt]
+ self.assertIn(f'{opt}={tmp}', ddclient_conf)
+
+ def test_04_dyndns_rfc2136(self):
+ # Check if DDNS service can be configured and runs
+ ddns = ['address', interface, 'rfc2136', 'vyos']
+ srv = 'ns1.vyos.io'
+ zone = 'vyos.io'
+ ttl = '300'
+
+ with tempfile.NamedTemporaryFile(prefix='/config/auth/') as key_file:
+ key_file.write(b'S3cretKey')
+
+ self.cli_set(base_path + ddns + ['server', srv])
+ self.cli_set(base_path + ddns + ['zone', zone])
+ self.cli_set(base_path + ddns + ['key', key_file.name])
+ self.cli_set(base_path + ddns + ['ttl', ttl])
+ self.cli_set(base_path + ddns + ['host-name', hostname])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check some generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ self.assertIn(f'use=if', ddclient_conf)
+ self.assertIn(f'if={interface}', ddclient_conf)
+ self.assertIn(f'protocol=nsupdate', ddclient_conf)
+ self.assertIn(f'server={srv}', ddclient_conf)
+ self.assertIn(f'zone={zone}', ddclient_conf)
+ self.assertIn(f'password={key_file.name}', ddclient_conf)
+ self.assertIn(f'ttl={ttl}', ddclient_conf)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py
index 88492e348..bc50a4ffe 100755
--- a/smoketest/scripts/cli/test_service_dns_forwarding.py
+++ b/smoketest/scripts/cli/test_service_dns_forwarding.py
@@ -21,8 +21,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.template import bracketize_ipv6
-from vyos.util import read_file
-from vyos.util import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
CONFIG_FILE = '/run/powerdns/recursor.conf'
FORWARD_FILE = '/run/powerdns/recursor.forward-zones.conf'
@@ -256,4 +256,4 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
self.assertEqual(tmp, port)
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 1adf1f5cf..1ae5c104c 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-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,8 @@ from urllib3.exceptions import InsecureRequestWarning
from base_vyostest_shim import VyOSUnitTestSHIM
from base_vyostest_shim import ignore_warning
-from vyos.util import read_file
-from vyos.util import run
+from vyos.utils.file import read_file
+from vyos.utils.process import run
base_path = ['service', 'https']
pki_base = ['pki']
diff --git a/smoketest/scripts/cli/test_service_ids.py b/smoketest/scripts/cli/test_service_ids.py
index dcf2bcefe..91b056eea 100755
--- a/smoketest/scripts/cli/test_service_ids.py
+++ b/smoketest/scripts/cli/test_service_ids.py
@@ -20,8 +20,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'fastnetmon'
FASTNETMON_CONF = '/run/fastnetmon/fastnetmon.conf'
diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py
index 8a141b8f0..4dd3e761c 100755
--- a/smoketest/scripts/cli/test_service_ipoe-server.py
+++ b/smoketest/scripts/cli/test_service_ipoe-server.py
@@ -19,7 +19,7 @@ import unittest
from base_accel_ppp_test import BasicAccelPPPTest
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
+from vyos.utils.process import cmd
from configparser import ConfigParser
diff --git a/smoketest/scripts/cli/test_service_lldp.py b/smoketest/scripts/cli/test_service_lldp.py
index 439c96c33..ee26844ab 100755
--- a/smoketest/scripts/cli/test_service_lldp.py
+++ b/smoketest/scripts/cli/test_service_lldp.py
@@ -22,9 +22,9 @@ 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.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
from vyos.version import get_version_data
PROCESS_NAME = 'lldpd'
diff --git a/smoketest/scripts/cli/test_service_mdns-repeater.py b/smoketest/scripts/cli/test_service_mdns-repeater.py
index f99a98da1..9a9839025 100755
--- a/smoketest/scripts/cli/test_service_mdns-repeater.py
+++ b/smoketest/scripts/cli/test_service_mdns-repeater.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,30 +18,57 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import process_named_running
+from configparser import ConfigParser
+from vyos.utils.process import process_named_running
base_path = ['service', 'mdns', 'repeater']
intf_base = ['interfaces', 'dummy']
+config_file = '/run/avahi-daemon/avahi-daemon.conf'
+
class TestServiceMDNSrepeater(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running('avahi-daemon'))
+
self.cli_delete(base_path)
self.cli_delete(intf_base + ['dum10'])
self.cli_delete(intf_base + ['dum20'])
self.cli_commit()
+ # Check that there is no longer a running process
+ self.assertFalse(process_named_running('avahi-daemon'))
+
def test_service(self):
- # Service required a configured IP address on the interface
+ # mDNS browsing domains in addition to the default one (local)
+ domains = ['dom1.home.arpa', 'dom2.home.arpa']
+
+ # mDNS services to be repeated
+ services = ['_ipp._tcp', '_smb._tcp', '_ssh._tcp']
+ # Service required a configured IP address on the interface
self.cli_set(intf_base + ['dum10', 'address', '192.0.2.1/30'])
self.cli_set(intf_base + ['dum20', 'address', '192.0.2.5/30'])
self.cli_set(base_path + ['interface', 'dum10'])
self.cli_set(base_path + ['interface', 'dum20'])
+
+ for domain in domains:
+ self.cli_set(base_path + ['browse-domain', domain])
+
+ for service in services:
+ self.cli_set(base_path + ['allow-service', service])
+
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running('avahi-daemon'))
+ # Validate configuration values
+ conf = ConfigParser(delimiters='=')
+ conf.read(config_file)
+
+ self.assertEqual(conf['server']['allow-interfaces'], 'dum10, dum20')
+ self.assertEqual(conf['server']['browse-domains'], ', '.join(domains))
+ self.assertEqual(conf['reflector']['enable-reflector'], 'yes')
+ self.assertEqual(conf['reflector']['reflect-filters'], ', '.join(services))
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 ed486c3b9..f3355b735 100755
--- a/smoketest/scripts/cli/test_service_monitoring_telegraf.py
+++ b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
@@ -20,8 +20,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'telegraf'
TELEGRAF_CONF = '/run/telegraf/telegraf.conf'
diff --git a/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py b/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py
new file mode 100755
index 000000000..cb5f84406
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
+
+
+PROCESS_NAME = 'zabbix_agent2'
+ZABBIX_AGENT_CONF = '/run/zabbix/zabbix-agent2.conf'
+base_path = ['service', 'monitoring', 'zabbix-agent']
+
+
+class TestZabbixAgent(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Process must be terminated after deleting the config
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_zabbix_agent(self):
+ directory = '/tmp'
+ buffer_send = '8'
+ buffer_size = '120'
+ log_level = {'warning': '3'}
+ log_size = '1'
+ servers = ['192.0.2.1', '2001:db8::1']
+ servers_active = {'192.0.2.5': {'port': '10051'}, '2001:db8::123': {'port': '10052'}}
+ port = '10050'
+ timeout = '5'
+ listen_ip = '0.0.0.0'
+ hostname = 'r-vyos'
+
+ self.cli_set(base_path + ['directory', directory])
+ self.cli_set(base_path + ['limits', 'buffer-flush-interval', buffer_send])
+ self.cli_set(base_path + ['limits', 'buffer-size', buffer_size])
+ self.cli_set(base_path + ['log', 'debug-level', next(iter(log_level))])
+ self.cli_set(base_path + ['log', 'size', log_size])
+ for server in servers:
+ self.cli_set(base_path + ['server', server])
+ for server_active, server_config in servers_active.items():
+ self.cli_set(base_path + ['server-active', server_active, 'port', server_config['port']])
+ self.cli_set(base_path + ['timeout', timeout])
+ self.cli_set(base_path + ['host-name', hostname])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(ZABBIX_AGENT_CONF)
+
+ self.assertIn(f'LogFileSize={log_size}', config)
+ self.assertIn(f'DebugLevel={log_level.get("warning")}', config)
+
+ self.assertIn(f'Server={",".join(sorted(servers))}', config)
+ tmp = 'ServerActive=192.0.2.5:10051,[2001:db8::123]:10052'
+ self.assertIn(tmp, config)
+
+ self.assertIn(f'ListenPort={port}', config)
+ self.assertIn(f'ListenIP={listen_ip}', config)
+ self.assertIn(f'BufferSend={buffer_send}', config)
+ self.assertIn(f'BufferSize={buffer_size}', config)
+ self.assertIn(f'Include={directory}/*.conf', config)
+ self.assertIn(f'Timeout={timeout}', config)
+ self.assertIn(f'Hostname={hostname}', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ntp.py b/smoketest/scripts/cli/test_service_ntp.py
index 046e5eea6..5e385d5ad 100755
--- a/smoketest/scripts/cli/test_service_ntp.py
+++ b/smoketest/scripts/cli/test_service_ntp.py
@@ -19,8 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import process_named_running
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'chronyd'
NTP_CONF = '/run/chrony/chrony.conf'
@@ -108,7 +108,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'bindaddress {listen}', config)
def test_03_ntp_interface(self):
- interfaces = ['eth0', 'eth1']
+ interfaces = ['eth0']
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index bb6a1c1cd..963784f0a 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -19,7 +19,7 @@ import unittest
from base_accel_ppp_test import BasicAccelPPPTest
from configparser import ConfigParser
-from vyos.util import read_file
+from vyos.utils.file import read_file
from vyos.template import range_to_regex
local_if = ['interfaces', 'dummy', 'dum667']
diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py
index 0169b7934..5fc2019fd 100755
--- a/smoketest/scripts/cli/test_service_router-advert.py
+++ b/smoketest/scripts/cli/test_service_router-advert.py
@@ -20,8 +20,8 @@ import unittest
from vyos.configsession import ConfigSessionError
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import read_file
-from vyos.util import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
PROCESS_NAME = 'radvd'
RADVD_CONF = '/run/radvd/radvd.conf'
diff --git a/smoketest/scripts/cli/test_service_salt.py b/smoketest/scripts/cli/test_service_salt.py
index 00a4f2020..48a588b72 100755
--- a/smoketest/scripts/cli/test_service_salt.py
+++ b/smoketest/scripts/cli/test_service_salt.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,9 +19,9 @@ import unittest
from socket import gethostname
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import process_named_running
-from vyos.util import read_file
-from vyos.util import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.process import cmd
PROCESS_NAME = 'salt-minion'
SALT_CONF = '/etc/salt/minion'
diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py
index b18b9e7a1..52a72ec4f 100755
--- a/smoketest/scripts/cli/test_service_snmp.py
+++ b/smoketest/scripts/cli/test_service_snmp.py
@@ -22,10 +22,10 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
from vyos.template import address_from_cidr
-from vyos.util import call
-from vyos.util import DEVNULL
-from vyos.util import read_file
-from vyos.util import process_named_running
+from vyos.utils.process import call
+from vyos.utils.process import DEVNULL
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
from vyos.version import get_version_data
PROCESS_NAME = 'snmpd'
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index 8de98f34f..947d7d568 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -24,10 +24,10 @@ 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
+from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'sshd'
SSHD_CONF = '/run/sshd/sshd_config'
@@ -174,18 +174,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
#
# 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'
@@ -197,14 +185,14 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Login with proper credentials
- output, error = ssh_send_cmd(test_command, test_user, test_pass)
+ output, error = self.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')
+ output, error = self.ssh_send_cmd(test_command, 'invalid_user', 'invalid_password')
self.cli_delete(['system', 'login', 'user', test_user])
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py
index 99d81e203..d60794980 100755
--- a/smoketest/scripts/cli/test_service_tftp-server.py
+++ b/smoketest/scripts/cli/test_service_tftp-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,9 +20,9 @@ from psutil import process_iter
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import read_file
-from vyos.util import process_named_running
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
from vyos.template import is_ipv6
PROCESS_NAME = 'in.tftpd'
diff --git a/smoketest/scripts/cli/test_service_upnp.py b/smoketest/scripts/cli/test_service_upnp.py
index e4df88c1e..c3fb0ec9d 100755
--- a/smoketest/scripts/cli/test_service_upnp.py
+++ b/smoketest/scripts/cli/test_service_upnp.py
@@ -22,8 +22,8 @@ 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
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
UPNP_CONF = '/run/upnp/miniupnp.conf'
DAEMON = 'miniupnpd'
diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py
index fb9b46a06..2b3f6d21c 100755
--- a/smoketest/scripts/cli/test_service_webproxy.py
+++ b/smoketest/scripts/cli/test_service_webproxy.py
@@ -19,9 +19,9 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'squid'
PROXY_CONF = '/etc/squid/squid.conf'
diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py
index fd16146b1..2a89aa98b 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.firewall import find_nftables_rule
-from vyos.util import cmd
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
base_path = ['system', 'conntrack']
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index df60b9613..d55ea616e 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -22,9 +22,9 @@ from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.template import bracketize_ipv6
from vyos.template import is_ipv6
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'uacctd'
base_path = ['system', 'flow-accounting']
diff --git a/smoketest/scripts/cli/test_system_frr.py b/smoketest/scripts/cli/test_system_frr.py
index 331133ed4..3eb0cd0ab 100755
--- a/smoketest/scripts/cli/test_system_frr.py
+++ b/smoketest/scripts/cli/test_system_frr.py
@@ -17,7 +17,7 @@
import re
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import read_file
+from vyos.utils.file import read_file
config_file = '/etc/frr/daemons'
base_path = ['system', 'frr']
@@ -143,4 +143,4 @@ class TestSystemFRR(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index e7f7e3345..567416774 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -18,7 +18,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import read_file
+from vyos.utils.file import read_file
base_path = ['system', 'ip']
@@ -98,6 +98,16 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
for protocol in protocols:
self.assertIn(f'ip protocol {protocol} route-map route-map-{protocol}', frrconfig)
+ # Delete route-maps
+ self.cli_delete(['policy', 'route-map'])
+ self.cli_delete(base_path + ['protocol'])
+
+ self.cli_commit()
+
+ # Verify route-map properly applied to FRR
+ frrconfig = self.getFRRconfig('ip protocol', end='', daemon='zebra')
+ self.assertNotIn(f'ip protocol', frrconfig)
+
def test_system_ip_protocol_non_existing_route_map(self):
non_existing = 'non-existing'
self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing])
@@ -106,6 +116,7 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny'])
+
# Commit again
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index e91b924fc..225c2d666 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -20,9 +20,9 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
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
+from vyos.utils.file import read_file
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import is_intf_addr_assigned
base_path = ['system', 'ipv6']
@@ -109,6 +109,16 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
protocol = 'ospf6'
self.assertIn(f'ipv6 protocol {protocol} route-map route-map-{protocol}', frrconfig)
+ # Delete route-maps
+ self.cli_delete(['policy', 'route-map'])
+ self.cli_delete(base_path + ['protocol'])
+
+ self.cli_commit()
+
+ # Verify route-map properly applied to FRR
+ frrconfig = self.getFRRconfig('ipv6 protocol', end='', daemon='zebra')
+ self.assertNotIn(f'ipv6 protocol', frrconfig)
+
def test_system_ipv6_protocol_non_existing_route_map(self):
non_existing = 'non-existing6'
self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing])
@@ -117,6 +127,7 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny'])
+
# Commit again
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_system_lcd.py b/smoketest/scripts/cli/test_system_lcd.py
index 831fba979..fc440ca8a 100755
--- a/smoketest/scripts/cli/test_system_lcd.py
+++ b/smoketest/scripts/cli/test_system_lcd.py
@@ -19,7 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from configparser import ConfigParser
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
config_file = '/run/LCDd/LCDd.conf'
base_path = ['system', 'lcd']
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index a1d2ba2ad..195b127a4 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -17,17 +17,17 @@
import re
import platform
import unittest
+import paramiko
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 time import sleep
from vyos.configsession import ConfigSessionError
-from vyos.util import cmd
-from vyos.util import read_file
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
from vyos.template import inc_ip
base_path = ['system', 'login']
@@ -53,12 +53,16 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
# ensure we can also run this test on a live system - so lets clean
# out the current configuration which will break this test
cls.cli_delete(cls, base_path + ['radius'])
+ cls.cli_delete(cls, base_path + ['tacacs'])
def tearDown(self):
# Delete individual users from configuration
for user in users:
self.cli_delete(base_path + ['user', user])
+ self.cli_delete(base_path + ['radius'])
+ self.cli_delete(base_path + ['tacacs'])
+
self.cli_commit()
# After deletion, a user is not allowed to remain in /etc/passwd
@@ -149,9 +153,6 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
# T2886 - RADIUS authentication - check for statically compiled options
options = ['CONFIG_AUDIT', 'CONFIG_AUDITSYSCALL', 'CONFIG_AUDIT_ARCH']
- if LooseVersion(kernel_version()) < LooseVersion('5.0'):
- options.append('CONFIG_AUDIT_WATCH')
- options.append('CONFIG_AUDIT_TREE')
for option in options:
self.assertIn(f'{option}=y', kernel_config)
@@ -284,6 +285,41 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path + ['timeout'])
self.cli_delete(base_path + ['max-login-session'])
+ def test_system_login_tacacs(self):
+ tacacs_secret = 'tac_plus_key'
+ tacacs_servers = ['100.64.0.11', '100.64.0.12']
+
+ # Enable TACACS
+ for server in tacacs_servers:
+ self.cli_set(base_path + ['tacacs', 'server', server, 'key', tacacs_secret])
+
+ self.cli_commit()
+
+ # NSS
+ nsswitch_conf = read_file('/etc/nsswitch.conf')
+ tmp = re.findall(r'passwd:\s+tacplus\s+files', nsswitch_conf)
+ self.assertTrue(tmp)
+
+ tmp = re.findall(r'group:\s+tacplus\s+files', nsswitch_conf)
+ self.assertTrue(tmp)
+
+ # PAM TACACS configuration
+ pam_tacacs_conf = read_file('/etc/tacplus_servers')
+ # NSS TACACS configuration
+ nss_tacacs_conf = read_file('/etc/tacplus_nss.conf')
+ # Users have individual home directories
+ self.assertIn('user_homedir=1', pam_tacacs_conf)
+
+ # specify services
+ self.assertIn('service=shell', pam_tacacs_conf)
+ self.assertIn('protocol=ssh', pam_tacacs_conf)
+
+ for server in tacacs_servers:
+ self.assertIn(f'secret={tacacs_secret}', pam_tacacs_conf)
+ self.assertIn(f'server={server}', pam_tacacs_conf)
+
+ self.assertIn(f'secret={tacacs_secret}', nss_tacacs_conf)
+ self.assertIn(f'server={server}', nss_tacacs_conf)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_logs.py b/smoketest/scripts/cli/test_system_logs.py
index 92fa9c3d9..17cce5ca1 100755
--- a/smoketest/scripts/cli/test_system_logs.py
+++ b/smoketest/scripts/cli/test_system_logs.py
@@ -17,7 +17,7 @@
import re
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import read_file
+from vyos.utils.file import read_file
# path to logrotate configs
logrotate_atop_file = '/etc/logrotate.d/vyos-atop'
diff --git a/smoketest/scripts/cli/test_system_nameserver.py b/smoketest/scripts/cli/test_system_nameserver.py
index 58c84988e..4979a7c72 100755
--- a/smoketest/scripts/cli/test_system_nameserver.py
+++ b/smoketest/scripts/cli/test_system_nameserver.py
@@ -21,7 +21,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.util import read_file
+from vyos.utils.file import read_file
RESOLV_CONF = '/etc/resolv.conf'
diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py
index 1aec050a4..63262db69 100755
--- a/smoketest/scripts/cli/test_system_sflow.py
+++ b/smoketest/scripts/cli/test_system_sflow.py
@@ -20,9 +20,9 @@ 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.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
PROCESS_NAME = 'hsflowd'
base_path = ['system', 'sflow']
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index b677f0e45..01b0406bf 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -18,9 +18,9 @@ import os
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.util import call
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import call
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
ethernet_path = ['interfaces', 'ethernet']
tunnel_path = ['interfaces', 'tunnel']
@@ -41,7 +41,7 @@ vif = '100'
esp_group = 'MyESPGroup'
ike_group = 'MyIKEGroup'
secret = 'MYSECRETKEY'
-PROCESS_NAME = 'charon'
+PROCESS_NAME = 'charon-systemd'
regex_uuid4 = '[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}'
ca_pem = """
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index ec8ecacb9..04abeb1aa 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -19,8 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.template import ip_from_cidr
-from vyos.util import process_named_running
-from vyos.util import read_file
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
OCSERV_CONF = '/run/ocserv/ocserv.conf'
base_path = ['vpn', 'openconnect']
diff --git a/smoketest/scripts/cli/test_vpn_sstp.py b/smoketest/scripts/cli/test_vpn_sstp.py
index 434e3aa05..232eafcf2 100755
--- a/smoketest/scripts/cli/test_vpn_sstp.py
+++ b/smoketest/scripts/cli/test_vpn_sstp.py
@@ -17,7 +17,7 @@
import unittest
from base_accel_ppp_test import BasicAccelPPPTest
-from vyos.util import read_file
+from vyos.utils.file import read_file
pki_path = ['pki']
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 926616727..0f658f366 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -26,10 +26,10 @@ from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
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
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import is_intf_addr_assigned
base_path = ['vrf']
vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo']
@@ -47,9 +47,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
tmp = os.environ['TEST_ETH'].split()
cls._interfaces = tmp
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._interfaces.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._interfaces.append(tmp)
# call base-classes classmethod
super(VRFTest, cls).setUpClass()
@@ -316,6 +315,19 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
for protocol in v4_protocols:
self.assertIn(f' ip protocol {protocol} route-map route-map-{vrf}-{protocol}', frrconfig)
+ # Delete route-maps
+ for vrf in vrfs:
+ base = base_path + ['name', vrf]
+ self.cli_delete(['policy', 'route-map', f'route-map-{vrf}-{protocol}'])
+ self.cli_delete(base + ['ip', 'protocol'])
+
+ self.cli_commit()
+
+ # Verify route-map properly is removed from FRR
+ for vrf in vrfs:
+ frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon='zebra')
+ self.assertNotIn(f'vrf {vrf}', frrconfig)
+
def test_vrf_ip_ipv6_protocol_non_existing_route_map(self):
table = '6100'
non_existing = 'non-existing'
@@ -370,6 +382,19 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
route_map = f'route-map-{vrf}-{protocol}'
self.assertIn(f' ipv6 protocol {protocol} route-map {route_map}', frrconfig)
+ # Delete route-maps
+ for vrf in vrfs:
+ base = base_path + ['name', vrf]
+ self.cli_delete(['policy', 'route-map', f'route-map-{vrf}-{protocol}'])
+ self.cli_delete(base + ['ipv6', 'protocol'])
+
+ self.cli_commit()
+
+ # Verify route-map properly is removed from FRR
+ for vrf in vrfs:
+ frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon='zebra')
+ self.assertNotIn(f'vrf {vrf}', frrconfig)
+
def test_vrf_vni_duplicates(self):
base_table = '6300'
table = base_table
@@ -464,4 +489,4 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2, failfast=True)
diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py
index fe2a1c48a..a39ae50bc 100755
--- a/smoketest/scripts/system/test_kernel_options.py
+++ b/smoketest/scripts/system/test_kernel_options.py
@@ -20,14 +20,13 @@ import os
import platform
import unittest
-from vyos.util import call
-from vyos.util import read_file
+from vyos.utils.process import call
+from vyos.utils.file import read_file
kernel = platform.release()
config = read_file(f'/boot/config-{kernel}')
CONFIG = '/proc/config.gz'
-
class TestKernelModules(unittest.TestCase):
""" VyOS makes use of a lot of Kernel drivers, modules and features. The
required modules which are essential for VyOS should be tested that they are
diff --git a/smoketest/scripts/system/test_module_load.py b/smoketest/scripts/system/test_module_load.py
index bd30c57ec..9d94f01e6 100755
--- a/smoketest/scripts/system/test_module_load.py
+++ b/smoketest/scripts/system/test_module_load.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
-from vyos.util import cmd
+from vyos.utils.process import cmd
modules = {
"intel": ["e1000", "e1000e", "igb", "ixgb", "ixgbe", "ixgbevf", "i40e",
@@ -23,7 +23,8 @@ modules = {
"intel_qat": ["qat_200xx", "qat_200xxvf", "qat_c3xxx", "qat_c3xxxvf",
"qat_c62x", "qat_c62xvf", "qat_d15xx", "qat_d15xxvf",
"qat_dh895xcc", "qat_dh895xccvf"],
- "accel_ppp": ["ipoe", "vlan_mon"]
+ "accel_ppp": ["ipoe", "vlan_mon"],
+ "openvpn": ["ovpn-dco-v2"]
}
class TestKernelModules(unittest.TestCase):
diff --git a/src/completion/list_dumpable_interfaces.py b/src/completion/list_dumpable_interfaces.py
index 67bf6206b..f9748352f 100755
--- a/src/completion/list_dumpable_interfaces.py
+++ b/src/completion/list_dumpable_interfaces.py
@@ -4,7 +4,7 @@
import re
-from vyos.util import cmd
+from vyos.utils.process import cmd
if __name__ == '__main__':
out = cmd('tcpdump -D').split('\n')
diff --git a/src/completion/list_ipoe.py b/src/completion/list_ipoe.py
index c386b46a2..5a8f4b0c5 100755
--- a/src/completion/list_ipoe.py
+++ b/src/completion/list_ipoe.py
@@ -1,7 +1,21 @@
#!/usr/bin/env python3
+# Copyright 2020-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import argparse
-from vyos.util import popen
+from vyos.utils.process import popen
if __name__ == '__main__':
parser = argparse.ArgumentParser()
diff --git a/src/completion/list_ipsec_profile_tunnels.py b/src/completion/list_ipsec_profile_tunnels.py
index df6c52f6d..4a917dbd6 100644
--- a/src/completion/list_ipsec_profile_tunnels.py
+++ b/src/completion/list_ipsec_profile_tunnels.py
@@ -19,7 +19,7 @@ import sys
import argparse
from vyos.config import Config
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
def get_tunnels_from_ipsecprofile(profile):
config = Config()
diff --git a/src/completion/list_openconnect_users.py b/src/completion/list_openconnect_users.py
index a266fd893..db2f4b4da 100755
--- a/src/completion/list_openconnect_users.py
+++ b/src/completion/list_openconnect_users.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from vyos.config import Config
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
def get_user_from_ocserv():
config = Config()
diff --git a/src/completion/list_openvpn_users.py b/src/completion/list_openvpn_users.py
index c472dbeab..b2b0149fc 100755
--- a/src/completion/list_openvpn_users.py
+++ b/src/completion/list_openvpn_users.py
@@ -19,7 +19,7 @@ import sys
import argparse
from vyos.config import Config
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
def get_user_from_interface(interface):
config = Config()
diff --git a/src/conf_mode/arp.py b/src/conf_mode/arp.py
index 7dc5206e0..b141f1141 100755
--- a/src/conf_mode/arp.py
+++ b/src/conf_mode/arp.py
@@ -18,7 +18,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import node_changed
-from vyos.util import call
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py
index 39a2971ce..31c552f5a 100755
--- a/src/conf_mode/bcast_relay.py
+++ b/src/conf_mode/bcast_relay.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017-2022 VyOS maintainers and contributors
+# Copyright (C) 2017-2023 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,14 @@
import os
from glob import glob
-from netifaces import interfaces
+from netifaces import AF_INET
from sys import exit
from vyos.config import Config
-from vyos.util import call
+from vyos.configverify import verify_interface_exists
from vyos.template import render
+from vyos.utils.process import call
+from vyos.utils.network import is_afi_configured
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -50,18 +52,16 @@ def verify(relay):
# we certainly require a UDP port to listen to
if 'port' not in config:
- raise ConfigError(f'Port number mandatory for udp broadcast relay "{instance}"')
+ raise ConfigError(f'Port number is mandatory for UDP broadcast relay "{instance}"')
- # if only oone interface is given it's a string -> move to list
- if isinstance(config.get('interface', []), str):
- config['interface'] = [ config['interface'] ]
# Relaying data without two interface is kinda senseless ...
if len(config.get('interface', [])) < 2:
- raise ConfigError('At least two interfaces are required for udp broadcast relay "{instance}"')
+ raise ConfigError('At least two interfaces are required for UDP broadcast relay "{instance}"')
for interface in config.get('interface', []):
- if interface not in interfaces():
- raise ConfigError('Interface "{interface}" does not exist!')
+ verify_interface_exists(interface)
+ if not is_afi_configured(interface, AF_INET):
+ raise ConfigError(f'Interface "{interface}" has no IPv4 address configured!')
return None
diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py
index 82289526f..9c43640a9 100755
--- a/src/conf_mode/conntrack.py
+++ b/src/conf_mode/conntrack.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,15 +20,13 @@ import re
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.firewall import find_nftables_rule
from vyos.firewall import remove_nftables_rule
-from vyos.util import cmd
-from vyos.util import run
-from vyos.util import process_named_running
-from vyos.util import dict_search
+from vyos.utils.process import process_named_running
+from vyos.utils.dict import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.process import run
from vyos.template import render
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -77,16 +75,8 @@ def get_config(config=None):
base = ['system', 'conntrack']
conntrack = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True)
-
- # 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)
+ get_first_key=True,
+ with_recursive_defaults=True)
return conntrack
diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py
index c4b2bb488..4fb2ce27f 100755
--- a/src/conf_mode/conntrack_sync.py
+++ b/src/conf_mode/conntrack_sync.py
@@ -18,17 +18,15 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configverify import verify_interface_exists
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import process_named_running
-from vyos.util import read_file
-from vyos.util import run
+from vyos.utils.dict import dict_search
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
+from vyos.utils.process import call
+from vyos.utils.process import run
from vyos.template import render
from vyos.template import get_ipv4
-from vyos.validate import is_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -50,11 +48,7 @@ def get_config(config=None):
return None
conntrack = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True)
- # 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)
- conntrack = dict_merge(default_values, conntrack)
+ get_first_key=True, with_defaults=True)
conntrack['hash_size'] = read_file('/sys/module/nf_conntrack/parameters/hashsize')
conntrack['table_size'] = read_file('/proc/sys/net/netfilter/nf_conntrack_max')
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index aceb27fb0..46eb10714 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -28,16 +28,17 @@ from vyos.configdict import node_changed
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.ifconfig import Interface
-from vyos.util import call
-from vyos.util import cmd
-from vyos.util import run
-from vyos.util import rc_cmd
-from vyos.util import write_file
+from vyos.utils.file import write_file
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.process import run
+from vyos.utils.process import rc_cmd
+from vyos.template import bracketize_ipv6
from vyos.template import inc_ip
from vyos.template import is_ipv4
from vyos.template import is_ipv6
from vyos.template import render
-from vyos.xml import defaults
+from vyos.xml_ref import default_value
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -66,58 +67,26 @@ def get_config(config=None):
base = ['container']
container = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base)
- # container base default values can not be merged here - remove and add them later
- if 'name' in default_values:
- del default_values['name']
- # registry will be handled below
- if 'registry' in default_values:
- del default_values['registry']
- container = dict_merge(default_values, container)
-
- # Merge per-container default values
- if 'name' in container:
- default_values = defaults(base + ['name'])
- if 'port' in default_values:
- del default_values['port']
- if 'volume' in default_values:
- del default_values['volume']
- for name in container['name']:
- container['name'][name] = dict_merge(default_values, container['name'][name])
-
- # T5047: Any container related configuration changed? We only
- # wan't to restart the required containers and not all of them ...
- tmp = is_node_changed(conf, base + ['name', name])
- if tmp:
- if 'container_restart' not in container:
- container['container_restart'] = [name]
- else:
- container['container_restart'].append(name)
-
- # 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 'port' in container['name'][name]:
- for port in container['name'][name]['port']:
- default_values_port = defaults(base + ['name', 'port'])
- container['name'][name]['port'][port] = dict_merge(
- default_values_port, container['name'][name]['port'][port])
- # 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 'volume' in container['name'][name]:
- for volume in container['name'][name]['volume']:
- default_values_volume = defaults(base + ['name', 'volume'])
- container['name'][name]['volume'][volume] = dict_merge(
- default_values_volume, container['name'][name]['volume'][volume])
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
+
+ for name in container.get('name', []):
+ # T5047: Any container related configuration changed? We only
+ # wan't to restart the required containers and not all of them ...
+ tmp = is_node_changed(conf, base + ['name', name])
+ if tmp:
+ if 'container_restart' not in container:
+ container['container_restart'] = [name]
+ else:
+ container['container_restart'].append(name)
# registry is a tagNode with default values - merge the list from
# default_values['registry'] into the tagNode variables
if 'registry' not in container:
container.update({'registry' : {}})
- default_values = defaults(base)
- for registry in default_values['registry'].split():
+ default_values = default_value(base + ['registry'])
+ for registry in default_values:
tmp = {registry : {}}
container['registry'] = dict_merge(tmp, container['registry'])
@@ -209,6 +178,11 @@ def verify(container):
if 'value' not in cfg:
raise ConfigError(f'Environment variable {var} has no value assigned!')
+ if 'label' in container_config:
+ for var, cfg in container_config['label'].items():
+ if 'value' not in cfg:
+ raise ConfigError(f'Label variable {var} has no value assigned!')
+
if 'volume' in container_config:
for volume, volume_config in container_config['volume'].items():
if 'source' not in volume_config:
@@ -299,6 +273,12 @@ def generate_run_arguments(name, container_config):
for k, v in container_config['environment'].items():
env_opt += f" --env \"{k}={v['value']}\""
+ # Check/set label options "--label foo=bar"
+ env_opt = ''
+ if 'label' in container_config:
+ for k, v in container_config['label'].items():
+ env_opt += f" --label \"{k}={v['value']}\""
+
hostname = ''
if 'host_name' in container_config:
hostname = container_config['host_name']
@@ -312,7 +292,15 @@ def generate_run_arguments(name, container_config):
protocol = container_config['port'][portmap]['protocol']
sport = container_config['port'][portmap]['source']
dport = container_config['port'][portmap]['destination']
- port += f' --publish {sport}:{dport}/{protocol}'
+ listen_addresses = container_config['port'][portmap].get('listen_address', [])
+
+ # If listen_addresses is not empty, include them in the publish command
+ if listen_addresses:
+ for listen_address in listen_addresses:
+ port += f' --publish {bracketize_ipv6(listen_address)}:{sport}:{dport}/{protocol}'
+ else:
+ # If listen_addresses is empty, just include the standard publish command
+ port += f' --publish {sport}:{dport}/{protocol}'
# Bind volume
volume = ''
@@ -321,7 +309,8 @@ def generate_run_arguments(name, container_config):
svol = vol_config['source']
dvol = vol_config['destination']
mode = vol_config['mode']
- volume += f' --volume {svol}:{dvol}:{mode}'
+ prop = vol_config['propagation']
+ volume += f' --volume {svol}:{dvol}:{mode},{prop}'
container_base_cmd = f'--detach --interactive --tty --replace {cap_add} ' \
f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \
@@ -485,6 +474,7 @@ def apply(container):
# it to a VRF as there's no consumer, yet.
if os.path.exists(f'/sys/class/net/{network_name}'):
tmp = Interface(network_name)
+ tmp.add_ipv6_eui64_address('fe80::/64')
tmp.set_vrf(network_config.get('vrf', ''))
return None
diff --git a/src/conf_mode/dhcp_relay.py b/src/conf_mode/dhcp_relay.py
index 7e702a446..37d708847 100755
--- a/src/conf_mode/dhcp_relay.py
+++ b/src/conf_mode/dhcp_relay.py
@@ -20,12 +20,10 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
from vyos.base import Warning
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -41,17 +39,15 @@ def get_config(config=None):
if not conf.exists(base):
return None
- relay = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- relay = dict_merge(default_values, relay)
+ relay = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return relay
def verify(relay):
# bail out early - looks like removal from running config
- if not relay:
+ if not relay or 'disable' in relay:
return None
if 'lo' in (dict_search('interface', relay) or []):
@@ -78,7 +74,7 @@ def verify(relay):
def generate(relay):
# bail out early - looks like removal from running config
- if not relay:
+ if not relay or 'disable' in relay:
return None
render(config_file, 'dhcp-relay/dhcrelay.conf.j2', relay)
@@ -87,7 +83,7 @@ def generate(relay):
def apply(relay):
# bail out early - looks like removal from running config
service_name = 'isc-dhcp-relay.service'
- if not relay:
+ if not relay or 'disable' in relay:
call(f'systemctl stop {service_name}')
if os.path.exists(config_file):
os.unlink(config_file)
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index 2b2af252d..c4c72aae9 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -23,14 +23,12 @@ from netaddr import IPRange
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import run
-from vyos.validate import is_subnet_connected
-from vyos.validate import is_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
+from vyos.utils.process import call
+from vyos.utils.process import run
+from vyos.utils.network import is_subnet_connected
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -109,19 +107,15 @@ def get_config(config=None):
if not conf.exists(base):
return None
- dhcp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
- # T2665: defaults include lease time per TAG node which need to be added to
- # individual subnet definitions
- default_values = defaults(base + ['shared-network-name', 'subnet'])
+ dhcp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
if 'shared_network_name' in dhcp:
for network, network_config in dhcp['shared_network_name'].items():
if 'subnet' in network_config:
for subnet, subnet_config in network_config['subnet'].items():
- if 'lease' not in subnet_config:
- dhcp['shared_network_name'][network]['subnet'][subnet] = dict_merge(
- default_values, dhcp['shared_network_name'][network]['subnet'][subnet])
-
# If exclude IP addresses are defined we need to slice them out of
# the defined ranges
if {'exclude', 'range'} <= set(subnet_config):
@@ -302,6 +296,10 @@ def generate(dhcp):
render(config_file, 'dhcp-server/dhcpd.conf.j2', dhcp,
formater=lambda _: _.replace("&quot;", '"'))
+ # Clean up configuration test file
+ if os.path.exists(tmp_file):
+ os.unlink(tmp_file)
+
return None
def apply(dhcp):
diff --git a/src/conf_mode/dhcpv6_relay.py b/src/conf_mode/dhcpv6_relay.py
index c1bd51f62..6537ca3c2 100755
--- a/src/conf_mode/dhcpv6_relay.py
+++ b/src/conf_mode/dhcpv6_relay.py
@@ -19,13 +19,11 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.ifconfig import Interface
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.validate import is_ipv6_link_local
-from vyos.xml import defaults
+from vyos.template import is_ipv6
+from vyos.utils.process import call
+from vyos.utils.network import is_ipv6_link_local
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -41,17 +39,15 @@ def get_config(config=None):
if not conf.exists(base):
return None
- relay = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- relay = dict_merge(default_values, relay)
+ relay = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return relay
def verify(relay):
# bail out early - looks like removal from running config
- if not relay:
+ if not relay or 'disable' in relay:
return None
if 'upstream_interface' not in relay:
@@ -69,7 +65,7 @@ def verify(relay):
for interface in relay['listen_interface']:
has_global = False
for addr in Interface(interface).get_addr():
- if not is_ipv6_link_local(addr):
+ if is_ipv6(addr) and not is_ipv6_link_local(addr):
has_global = True
if not has_global:
raise ConfigError(f'Interface {interface} does not have global '\
@@ -79,7 +75,7 @@ def verify(relay):
def generate(relay):
# bail out early - looks like removal from running config
- if not relay:
+ if not relay or 'disable' in relay:
return None
render(config_file, 'dhcp-relay/dhcrelay6.conf.j2', relay)
@@ -88,7 +84,7 @@ def generate(relay):
def apply(relay):
# bail out early - looks like removal from running config
service_name = 'isc-dhcp-relay6.service'
- if not relay:
+ if not relay or 'disable' in relay:
# DHCPv6 relay support is removed in the commit
call(f'systemctl stop {service_name}')
if os.path.exists(config_file):
diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index 078ff327c..427001609 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -23,9 +23,9 @@ from sys import exit
from vyos.config import Config
from vyos.template import render
from vyos.template import is_ipv6
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.validate import is_subnet_connected
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.network import is_subnet_connected
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py
new file mode 100755
index 000000000..ab80defe8
--- /dev/null
+++ b/src/conf_mode/dns_dynamic.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+
+from vyos.config import Config
+from vyos.template import render
+from vyos.utils.process import call
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+config_file = r'/run/ddclient/ddclient.conf'
+systemd_override = r'/run/systemd/system/ddclient.service.d/override.conf'
+
+# Protocols that require zone
+zone_allowed = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
+
+# Protocols that do not require username
+username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla']
+
+# Protocols that support both IPv4 and IPv6
+dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla']
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base_level = ['service', 'dns', 'dynamic']
+ if not conf.exists(base_level):
+ return None
+
+ dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
+
+ dyndns['config_file'] = config_file
+ return dyndns
+
+def verify(dyndns):
+ # bail out early - looks like removal from running config
+ if not dyndns or 'address' not in dyndns:
+ return None
+
+ for address in dyndns['address']:
+ # RFC2136 - configuration validation
+ if 'rfc2136' in dyndns['address'][address]:
+ for config in dyndns['address'][address]['rfc2136'].values():
+ for field in ['host_name', 'zone', 'server', 'key']:
+ if field not in config:
+ raise ConfigError(f'"{field.replace("_", "-")}" is required for RFC2136 '
+ f'based Dynamic DNS service on "{address}"')
+
+ # Dynamic DNS service provider - configuration validation
+ if 'service' in dyndns['address'][address]:
+ for service, config in dyndns['address'][address]['service'].items():
+ error_msg = f'is required for Dynamic DNS service "{service}" on "{address}"'
+
+ for field in ['host_name', 'password', 'protocol']:
+ if field not in config:
+ raise ConfigError(f'"{field.replace("_", "-")}" {error_msg}')
+
+ if config['protocol'] in zone_allowed and 'zone' not in config:
+ raise ConfigError(f'"zone" {error_msg}')
+
+ if config['protocol'] not in zone_allowed and 'zone' in config:
+ raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
+
+ if config['protocol'] not in username_unnecessary:
+ if 'username' not in config:
+ raise ConfigError(f'"username" {error_msg}')
+
+ if config['ip_version'] == 'both':
+ if config['protocol'] not in dualstack_supported:
+ raise ConfigError(f'"{config["protocol"]}" does not support '
+ f'both IPv4 and IPv6 at the same time')
+ # dyndns2 protocol in ddclient honors dual stack only for dyn.com (dyndns.org)
+ if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] != 'members.dyndns.org':
+ raise ConfigError(f'"{config["protocol"]}" does not support '
+ f'both IPv4 and IPv6 at the same time for "{config["server"]}"')
+
+ return None
+
+def generate(dyndns):
+ # bail out early - looks like removal from running config
+ if not dyndns or 'address' not in dyndns:
+ return None
+
+ render(config_file, 'dns-dynamic/ddclient.conf.j2', dyndns)
+ render(systemd_override, 'dns-dynamic/override.conf.j2', dyndns)
+ return None
+
+def apply(dyndns):
+ systemd_service = 'ddclient.service'
+ # Reload systemd manager configuration
+ call('systemctl daemon-reload')
+
+ # bail out early - looks like removal from running config
+ if not dyndns or 'address' not in dyndns:
+ call(f'systemctl stop {systemd_service}')
+ if os.path.exists(config_file):
+ os.unlink(config_file)
+ else:
+ call(f'systemctl reload-or-restart {systemd_service}')
+
+ 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/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 0d86c6a52..c186f47af 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -21,14 +21,12 @@ from sys import exit
from glob import glob
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.hostsd_client import Client as hostsd_client
from vyos.template import render
from vyos.template import bracketize_ipv6
-from vyos.util import call
-from vyos.util import chown
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.permission import chown
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
@@ -52,31 +50,10 @@ def get_config(config=None):
if not conf.exists(base):
return None
- dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrieved.
- default_values = defaults(base)
- # T2665 due to how defaults under tag nodes work, we must clear these out before we merge
- del default_values['authoritative_domain']
- del default_values['name_server']
- del default_values['domain']['name_server']
- dns = dict_merge(default_values, dns)
-
- # T2665: we cleared default values for tag node 'name_server' above.
- # We now need to add them back back in a granular way.
- if 'name_server' in dns:
- default_values = defaults(base + ['name-server'])
- for server in dns['name_server']:
- dns['name_server'][server] = dict_merge(default_values, dns['name_server'][server])
-
- # T2665: we cleared default values for tag node 'domain' above.
- # We now need to add them back back in a granular way.
- if 'domain' in dns:
- default_values = defaults(base + ['domain', 'name-server'])
- for domain in dns['domain'].keys():
- for server in dns['domain'][domain]['name_server']:
- dns['domain'][domain]['name_server'][server] = dict_merge(
- default_values, dns['domain'][domain]['name_server'][server])
+ dns = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
# some additions to the default dictionary
if 'system' in dns:
@@ -109,9 +86,6 @@ def get_config(config=None):
rdata = recorddata[rtype][subnode]
if rtype in [ 'a', 'aaaa' ]:
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- rdata = dict_merge(rdefaults, rdata)
-
if not 'address' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one address is required')
continue
@@ -127,9 +101,6 @@ def get_config(config=None):
'value': address
})
elif rtype in ['cname', 'ptr', 'ns']:
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- rdata = dict_merge(rdefaults, rdata)
-
if not 'target' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: target is required')
continue
@@ -141,18 +112,12 @@ def get_config(config=None):
'value': '{}.'.format(rdata['target'])
})
elif rtype == 'mx':
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- del rdefaults['server']
- rdata = dict_merge(rdefaults, rdata)
-
if not 'server' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one server is required')
continue
for servername in rdata['server']:
serverdata = rdata['server'][servername]
- serverdefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'server']) # T2665
- serverdata = dict_merge(serverdefaults, serverdata)
zone['records'].append({
'name': subnode,
'type': rtype.upper(),
@@ -160,9 +125,6 @@ def get_config(config=None):
'value': '{} {}.'.format(serverdata['priority'], servername)
})
elif rtype == 'txt':
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- rdata = dict_merge(rdefaults, rdata)
-
if not 'value' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one value is required')
continue
@@ -175,9 +137,6 @@ def get_config(config=None):
'value': "\"{}\"".format(value.replace("\"", "\\\""))
})
elif rtype == 'spf':
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- rdata = dict_merge(rdefaults, rdata)
-
if not 'value' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: value is required')
continue
@@ -189,19 +148,12 @@ def get_config(config=None):
'value': '"{}"'.format(rdata['value'].replace("\"", "\\\""))
})
elif rtype == 'srv':
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- del rdefaults['entry']
- rdata = dict_merge(rdefaults, rdata)
-
if not 'entry' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one entry is required')
continue
for entryno in rdata['entry']:
entrydata = rdata['entry'][entryno]
- entrydefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'entry']) # T2665
- entrydata = dict_merge(entrydefaults, entrydata)
-
if not 'hostname' in entrydata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: hostname is required for entry {entryno}')
continue
@@ -217,19 +169,12 @@ def get_config(config=None):
'value': '{} {} {} {}.'.format(entrydata['priority'], entrydata['weight'], entrydata['port'], entrydata['hostname'])
})
elif rtype == 'naptr':
- rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
- del rdefaults['rule']
- rdata = dict_merge(rdefaults, rdata)
-
-
if not 'rule' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one rule is required')
continue
for ruleno in rdata['rule']:
ruledata = rdata['rule'][ruleno]
- ruledefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'rule']) # T2665
- ruledata = dict_merge(ruledefaults, ruledata)
flags = ""
if 'lookup-srv' in ruledata:
flags += "S"
diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py
deleted file mode 100755
index 426e3d693..000000000
--- a/src/conf_mode/dynamic_dns.py
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-
-from sys import exit
-
-from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-config_file = r'/run/ddclient/ddclient.conf'
-
-# Mapping of service name to service protocol
-default_service_protocol = {
- 'afraid': 'freedns',
- 'changeip': 'changeip',
- 'cloudflare': 'cloudflare',
- 'dnspark': 'dnspark',
- 'dslreports': 'dslreports1',
- 'dyndns': 'dyndns2',
- 'easydns': 'easydns',
- 'namecheap': 'namecheap',
- 'noip': 'noip',
- 'sitelutions': 'sitelutions',
- 'zoneedit': 'zoneedit1'
-}
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- base_level = ['service', 'dns', 'dynamic']
- if not conf.exists(base_level):
- return None
-
- dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), get_first_key=True)
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- for interface in dyndns['interface']:
- if 'service' in dyndns['interface'][interface]:
- # 'Autodetect' protocol used by DynDNS service
- for service in dyndns['interface'][interface]['service']:
- if service in default_service_protocol:
- dyndns['interface'][interface]['service'][service].update(
- {'protocol' : default_service_protocol.get(service)})
- else:
- dyndns['interface'][interface]['service'][service].update(
- {'custom': ''})
-
- if 'rfc2136' in dyndns['interface'][interface]:
- default_values = defaults(base_level + ['interface', 'rfc2136'])
- for rfc2136 in dyndns['interface'][interface]['rfc2136']:
- dyndns['interface'][interface]['rfc2136'][rfc2136] = dict_merge(
- default_values, dyndns['interface'][interface]['rfc2136'][rfc2136])
-
- return dyndns
-
-def verify(dyndns):
- # bail out early - looks like removal from running config
- if not dyndns:
- return None
-
- # A 'node' corresponds to an interface
- if 'interface' not in dyndns:
- return None
-
- for interface in dyndns['interface']:
- # RFC2136 - configuration validation
- if 'rfc2136' in dyndns['interface'][interface]:
- for rfc2136, config in dyndns['interface'][interface]['rfc2136'].items():
-
- for tmp in ['record', 'zone', 'server', 'key']:
- if tmp not in config:
- raise ConfigError(f'"{tmp}" required for rfc2136 based '
- f'DynDNS service on "{interface}"')
-
- if not os.path.isfile(config['key']):
- raise ConfigError(f'"key"-file not found for rfc2136 based '
- f'DynDNS service on "{interface}"')
-
- # DynDNS service provider - configuration validation
- if 'service' in dyndns['interface'][interface]:
- for service, config in dyndns['interface'][interface]['service'].items():
- error_msg = f'required for DynDNS service "{service}" on "{interface}"'
- if 'host_name' not in config:
- raise ConfigError(f'"host-name" {error_msg}')
-
- if 'login' not in config:
- if service != 'cloudflare' and ('protocol' not in config or config['protocol'] != 'cloudflare'):
- raise ConfigError(f'"login" (username) {error_msg}, unless using CloudFlare')
-
- if 'password' not in config:
- raise ConfigError(f'"password" {error_msg}')
-
- if 'zone' in config:
- if service != 'cloudflare' and ('protocol' not in config or config['protocol'] != 'cloudflare'):
- raise ConfigError(f'"zone" option only supported with CloudFlare')
-
- if 'custom' in config:
- if 'protocol' not in config:
- raise ConfigError(f'"protocol" {error_msg}')
-
- if 'server' not in config:
- raise ConfigError(f'"server" {error_msg}')
-
- return None
-
-def generate(dyndns):
- # bail out early - looks like removal from running config
- if not dyndns:
- return None
-
- render(config_file, 'dynamic-dns/ddclient.conf.j2', dyndns)
- return None
-
-def apply(dyndns):
- if not dyndns:
- call('systemctl stop ddclient.service')
- if os.path.exists(config_file):
- os.unlink(config_file)
- else:
- call('systemctl restart ddclient.service')
-
- 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/firewall.py b/src/conf_mode/firewall.py
index 190587980..c3b1ee015 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -23,7 +23,6 @@ from sys import exit
from vyos.base import Warning
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.configdep import set_dependents, call_dependents
@@ -31,13 +30,12 @@ from vyos.configdep import set_dependents, call_dependents
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
-from vyos.util import call
-from vyos.util import cmd
-from vyos.util import dict_search_args
-from vyos.util import dict_search_recursive
-from vyos.util import process_named_running
-from vyos.util import rc_cmd
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search_args
+from vyos.utils.dict import dict_search_recursive
+from vyos.utils.process import process_named_running
+from vyos.utils.process import rc_cmd
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -56,7 +54,6 @@ sysfs_config = {
'log_martians': {'sysfs': '/proc/sys/net/ipv4/conf/all/log_martians'},
'receive_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_redirects'},
'send_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/send_redirects'},
- 'source_validation': {'sysfs': '/proc/sys/net/ipv4/conf/*/rp_filter', 'disable': '0', 'strict': '1', 'loose': '2'},
'syn_cookies': {'sysfs': '/proc/sys/net/ipv4/tcp_syncookies'},
'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337'}
}
@@ -97,19 +94,22 @@ def geoip_updated(conf, firewall):
updated = False
for key, path in dict_search_recursive(firewall, 'geoip'):
- set_name = f'GEOIP_CC_{path[1]}_{path[3]}'
- if path[0] == 'name':
+ set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}'
+ if (path[0] == 'ipv4'):
out['name'].append(set_name)
- elif path[0] == 'ipv6_name':
+ elif (path[0] == 'ipv6'):
+ set_name = f'GEOIP_CC6_{path[1]}_{path[2]}_{path[4]}'
out['ipv6_name'].append(set_name)
+
updated = True
if 'delete' in node_diff:
for key, path in dict_search_recursive(node_diff['delete'], 'geoip'):
- set_name = f'GEOIP_CC_{path[1]}_{path[3]}'
- if path[0] == 'name':
+ set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}'
+ if (path[0] == 'ipv4'):
out['deleted_name'].append(set_name)
- elif path[0] == 'ipv6-name':
+ elif (path[0] == 'ipv6'):
+ set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}'
out['deleted_ipv6_name'].append(set_name)
updated = True
@@ -125,54 +125,17 @@ def get_config(config=None):
conf = Config()
base = ['firewall']
- firewall = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # We have gathered the dict representation of the CLI, but there are
- # default options which we need to update into the dictionary retrived.
- # XXX: T2665: we currently have no nice way for defaults under tag
- # nodes, thus we load the defaults "by hand"
- default_values = defaults(base)
- for tmp in ['name', 'ipv6_name']:
- if tmp in default_values:
- del default_values[tmp]
-
- if 'zone' in default_values:
- del default_values['zone']
-
- firewall = dict_merge(default_values, firewall)
-
- # Merge in defaults for IPv4 ruleset
- if 'name' in firewall:
- default_values = defaults(base + ['name'])
- for name in firewall['name']:
- firewall['name'][name] = dict_merge(default_values,
- firewall['name'][name])
-
- # Merge in defaults for IPv6 ruleset
- if 'ipv6_name' in firewall:
- default_values = defaults(base + ['ipv6-name'])
- for ipv6_name in firewall['ipv6_name']:
- firewall['ipv6_name'][ipv6_name] = dict_merge(default_values,
- firewall['ipv6_name'][ipv6_name])
-
- if 'zone' in firewall:
- default_values = defaults(base + ['zone'])
- for zone in firewall['zone']:
- firewall['zone'][zone] = dict_merge(default_values, firewall['zone'][zone])
+ firewall = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
+
firewall['group_resync'] = bool('group' in firewall or node_changed(conf, base + ['group']))
if firewall['group_resync']:
# Update nat and policy-route as firewall groups were updated
set_dependents('group_resync', conf)
- if 'config_trap' in firewall and firewall['config_trap'] == 'enable':
- diff = get_config_diff(conf)
- firewall['trap_diff'] = diff.get_child_nodes_diff_str(base)
- firewall['trap_targets'] = conf.get_config_dict(['service', 'snmp', 'trap-target'],
- key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
firewall['geoip_updated'] = geoip_updated(conf, firewall)
fqdn_config_parse(firewall)
@@ -191,11 +154,11 @@ def verify_rule(firewall, rule_conf, ipv6):
raise ConfigError('jump-target defined, but action jump needed and it is not defined')
target = rule_conf['jump_target']
if not ipv6:
- if target not in dict_search_args(firewall, 'name'):
+ if target not in dict_search_args(firewall, 'ipv4', 'name'):
raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system')
else:
- if target not in dict_search_args(firewall, 'ipv6_name'):
- raise ConfigError(f'Invalid jump-target. Firewall ipv6-name {target} does not exist on the system')
+ if target not in dict_search_args(firewall, 'ipv6', 'name'):
+ raise ConfigError(f'Invalid jump-target. Firewall ipv6 name {target} does not exist on the system')
if 'queue_options' in rule_conf:
if 'queue' not in rule_conf['action']:
@@ -295,6 +258,11 @@ def verify_rule(firewall, rule_conf, ipv6):
if 'queue_threshold' in rule_conf['log_options'] and 'group' not in rule_conf['log_options']:
raise ConfigError('log-options queue-threshold defined, but log group is not define')
+ for direction in ['inbound_interface','outbound_interface']:
+ if direction in rule_conf:
+ if 'interface_name' in rule_conf[direction] and 'interface_group' in rule_conf[direction]:
+ raise ConfigError(f'Cannot specify both interface-group and interface-name for {direction}')
+
def verify_nested_group(group_name, group, groups, seen):
if 'include' not in group:
return
@@ -312,10 +280,6 @@ def verify_nested_group(group_name, group, groups, seen):
verify_nested_group(g, groups[g], groups, seen)
def verify(firewall):
- 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')
-
if 'group' in firewall:
for group_type in nested_group_types:
if group_type in firewall['group']:
@@ -323,95 +287,45 @@ def verify(firewall):
for group_name, group in groups.items():
verify_nested_group(group_name, group, groups, [])
- for name in ['name', 'ipv6_name']:
- if name in firewall:
- for name_id, name_conf in firewall[name].items():
- if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf:
- raise ConfigError('default-action set to jump, but no default-jump-target specified')
- if 'default_jump_target' in name_conf:
- target = name_conf['default_jump_target']
- if 'jump' not in name_conf['default_action']:
- raise ConfigError('default-jump-target defined,but default-action jump needed and it is not defined')
- if name_conf['default_jump_target'] == name_id:
- raise ConfigError(f'Loop detected on default-jump-target.')
- ## Now need to check that default-jump-target exists (other firewall chain/name)
- if target not in dict_search_args(firewall, name):
- raise ConfigError(f'Invalid jump-target. Firewall {name} {target} does not exist on the system')
-
- if 'rule' in name_conf:
- for rule_id, rule_conf in name_conf['rule'].items():
- verify_rule(firewall, rule_conf, name == 'ipv6_name')
-
- if 'interface' in firewall:
- for ifname, if_firewall in firewall['interface'].items():
- # verify ifname needs to be disabled, dynamic devices come up later
- # verify_interface_exists(ifname)
-
- for direction in ['in', 'out', 'local']:
- name = dict_search_args(if_firewall, direction, 'name')
- ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
-
- if name and dict_search_args(firewall, 'name', name) == None:
- raise ConfigError(f'Invalid firewall name "{name}" referenced on interface {ifname}')
-
- if ipv6_name and dict_search_args(firewall, 'ipv6_name', ipv6_name) == None:
- raise ConfigError(f'Invalid firewall ipv6-name "{ipv6_name}" referenced on interface {ifname}')
-
- local_zone = False
- zone_interfaces = []
-
- if 'zone' in firewall:
- for zone, zone_conf in firewall['zone'].items():
- if 'local_zone' not in zone_conf and 'interface' not in zone_conf:
- raise ConfigError(f'Zone "{zone}" has no interfaces and is not the local zone')
-
- if 'local_zone' in zone_conf:
- if local_zone:
- raise ConfigError('There cannot be multiple local zones')
- if 'interface' in zone_conf:
- raise ConfigError('Local zone cannot have interfaces assigned')
- if 'intra_zone_filtering' in zone_conf:
- raise ConfigError('Local zone cannot use intra-zone-filtering')
- local_zone = True
-
- if 'interface' in zone_conf:
- found_duplicates = [intf for intf in zone_conf['interface'] if intf in zone_interfaces]
-
- if found_duplicates:
- raise ConfigError(f'Interfaces cannot be assigned to multiple zones')
-
- zone_interfaces += zone_conf['interface']
-
- if 'intra_zone_filtering' in zone_conf:
- intra_zone = zone_conf['intra_zone_filtering']
-
- if len(intra_zone) > 1:
- raise ConfigError('Only one intra-zone-filtering action must be specified')
-
- if 'firewall' in intra_zone:
- v4_name = dict_search_args(intra_zone, 'firewall', 'name')
- if v4_name and not dict_search_args(firewall, 'name', v4_name):
- raise ConfigError(f'Firewall name "{v4_name}" does not exist')
-
- v6_name = dict_search_args(intra_zone, 'firewall', 'ipv6_name')
- if v6_name and not dict_search_args(firewall, 'ipv6_name', v6_name):
- raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
-
- if not v4_name and not v6_name:
- raise ConfigError('No firewall names specified for intra-zone-filtering')
-
- if 'from' in zone_conf:
- for from_zone, from_conf in zone_conf['from'].items():
- if from_zone not in firewall['zone']:
- raise ConfigError(f'Zone "{zone}" refers to a non-existent or deleted zone "{from_zone}"')
-
- v4_name = dict_search_args(from_conf, 'firewall', 'name')
- if v4_name and not dict_search_args(firewall, 'name', v4_name):
- raise ConfigError(f'Firewall name "{v4_name}" does not exist')
-
- v6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name')
- if v6_name and not dict_search_args(firewall, 'ipv6_name', v6_name):
- raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
+ if 'ipv4' in firewall:
+ for name in ['name','forward','input','output']:
+ if name in firewall['ipv4']:
+ for name_id, name_conf in firewall['ipv4'][name].items():
+ if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf:
+ raise ConfigError('default-action set to jump, but no default-jump-target specified')
+ if 'default_jump_target' in name_conf:
+ target = name_conf['default_jump_target']
+ if 'jump' not in name_conf['default_action']:
+ raise ConfigError('default-jump-target defined, but default-action jump needed and it is not defined')
+ if name_conf['default_jump_target'] == name_id:
+ raise ConfigError(f'Loop detected on default-jump-target.')
+ ## Now need to check that default-jump-target exists (other firewall chain/name)
+ if target not in dict_search_args(firewall['ipv4'], 'name'):
+ raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system')
+
+ if 'rule' in name_conf:
+ for rule_id, rule_conf in name_conf['rule'].items():
+ verify_rule(firewall, rule_conf, False)
+
+ if 'ipv6' in firewall:
+ for name in ['name','forward','input','output']:
+ if name in firewall['ipv6']:
+ for name_id, name_conf in firewall['ipv6'][name].items():
+ if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf:
+ raise ConfigError('default-action set to jump, but no default-jump-target specified')
+ if 'default_jump_target' in name_conf:
+ target = name_conf['default_jump_target']
+ if 'jump' not in name_conf['default_action']:
+ raise ConfigError('default-jump-target defined, but default-action jump needed and it is not defined')
+ if name_conf['default_jump_target'] == name_id:
+ raise ConfigError(f'Loop detected on default-jump-target.')
+ ## Now need to check that default-jump-target exists (other firewall chain/name)
+ if target not in dict_search_args(firewall['ipv6'], 'name'):
+ raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system')
+
+ if 'rule' in name_conf:
+ for rule_id, rule_conf in name_conf['rule'].items():
+ verify_rule(firewall, rule_conf, True)
return None
@@ -419,18 +333,16 @@ def generate(firewall):
if not os.path.exists(nftables_conf):
firewall['first_install'] = True
- if 'zone' in firewall:
- for local_zone, local_zone_conf in firewall['zone'].items():
- if 'local_zone' not in local_zone_conf:
- continue
+ # Determine if conntrack is needed
+ firewall['ipv4_conntrack_action'] = 'return'
+ firewall['ipv6_conntrack_action'] = 'return'
- local_zone_conf['from_local'] = {}
-
- for zone, zone_conf in firewall['zone'].items():
- if zone == local_zone or 'from' not in zone_conf:
- continue
- if local_zone in zone_conf['from']:
- local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone]
+ for rules, path in dict_search_recursive(firewall, 'rule'):
+ if any(('state' in rule_conf or 'connection_status' in rule_conf) for rule_conf in rules.values()):
+ if path[0] == 'ipv4':
+ firewall['ipv4_conntrack_action'] = 'accept'
+ elif path[0] == 'ipv6':
+ firewall['ipv6_conntrack_action'] = 'accept'
render(nftables_conf, 'firewall/nftables.j2', firewall)
return None
@@ -440,9 +352,8 @@ def apply_sysfs(firewall):
paths = glob(conf['sysfs'])
value = None
- if name in firewall:
- conf_value = firewall[name]
-
+ if name in firewall['global_options']:
+ conf_value = firewall['global_options'][name]
if conf_value in conf:
value = conf[conf_value]
elif conf_value == 'enable':
@@ -455,42 +366,6 @@ def apply_sysfs(firewall):
with open(path, 'w') as f:
f.write(value)
-def post_apply_trap(firewall):
- if 'first_install' in firewall:
- return None
-
- if 'config_trap' not in firewall or firewall['config_trap'] != 'enable':
- return None
-
- if not process_named_running('snmpd'):
- return None
-
- trap_username = os.getlogin()
-
- for host, target_conf in firewall['trap_targets'].items():
- community = target_conf['community'] if 'community' in target_conf else 'public'
- port = int(target_conf['port']) if 'port' in target_conf else 162
-
- base_cmd = f'snmptrap -v2c -c {community} {host}:{port} 0 {snmp_trap_mib}::{snmp_trap_name} '
-
- for change_type, changes in firewall['trap_diff'].items():
- for path_str, value in changes.items():
- objects = [
- f'mgmtEventUser s "{trap_username}"',
- f'mgmtEventSource i {snmp_event_source}',
- f'mgmtEventType i {snmp_change_type[change_type]}'
- ]
-
- if change_type == 'add':
- objects.append(f'mgmtEventCurrCfg s "{path_str} {value}"')
- elif change_type == 'delete':
- objects.append(f'mgmtEventPrevCfg s "{path_str} {value}"')
- elif change_type == 'change':
- objects.append(f'mgmtEventPrevCfg s "{path_str} {value[0]}"')
- objects.append(f'mgmtEventCurrCfg s "{path_str} {value[1]}"')
-
- cmd(base_cmd + ' '.join(objects))
-
def apply(firewall):
install_result, output = rc_cmd(f'nft -f {nftables_conf}')
if install_result == 1:
@@ -515,8 +390,6 @@ def apply(firewall):
print('Updating GeoIP. Please wait...')
geoip_update(firewall)
- post_apply_trap(firewall)
-
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index f67f1710e..71acd69fa 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-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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,20 +18,17 @@ import os
import re
from sys import exit
-import ipaddress
-
from ipaddress import ip_address
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.config import config_dict_merge
+from vyos.configverify import verify_vrf
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
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -130,30 +127,19 @@ def get_config(config=None):
flow_accounting = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
+ # We have gathered the dict representation of the CLI, but there are
+ # default values which we need to conditionally update into the
+ # dictionary retrieved.
+ default_values = conf.get_config_defaults(**flow_accounting.kwargs,
+ recursive=True)
- # delete individual flow type default - should only be added if user uses
- # this feature
+ # delete individual flow type defaults - should only be added if user
+ # sets this feature
for flow_type in ['sflow', 'netflow']:
- if flow_type in default_values:
+ if flow_type not in flow_accounting and flow_type in default_values:
del default_values[flow_type]
- flow_accounting = dict_merge(default_values, flow_accounting)
- for flow_type in ['sflow', 'netflow']:
- if flow_type in flow_accounting:
- default_values = defaults(base + [flow_type])
- # we need to merge individual server configurations
- if 'server' in default_values:
- del default_values['server']
- flow_accounting[flow_type] = dict_merge(default_values, flow_accounting[flow_type])
-
- if 'server' in flow_accounting[flow_type]:
- default_values = defaults(base + [flow_type, 'server'])
- for server in flow_accounting[flow_type]['server']:
- flow_accounting[flow_type]['server'][server] = dict_merge(
- default_values,flow_accounting[flow_type]['server'][server])
+ flow_accounting = config_dict_merge(default_values, flow_accounting)
return flow_accounting
@@ -194,6 +180,7 @@ def verify(flow_config):
sflow_collector_ipver = ip_address(server).version
# check if vrf is defined for Sflow
+ verify_vrf(flow_config)
sflow_vrf = None
if 'vrf' in flow_config:
sflow_vrf = flow_config['vrf']
@@ -211,7 +198,7 @@ def verify(flow_config):
if not is_addr_assigned(tmp, sflow_vrf):
raise ConfigError(f'Configured "sflow agent-address {tmp}" does not exist in the system!')
- # Check if configured netflow source-address exist in the system
+ # Check if configured sflow source-address exist in the system
if 'source_address' in flow_config['sflow']:
if not is_addr_assigned(flow_config['sflow']['source_address'], sflow_vrf):
tmp = flow_config['sflow']['source_address']
@@ -219,13 +206,18 @@ def verify(flow_config):
# check NetFlow configuration
if 'netflow' in flow_config:
+ # check if vrf is defined for netflow
+ netflow_vrf = None
+ if 'vrf' in flow_config:
+ netflow_vrf = flow_config['vrf']
+
# check if at least one NetFlow collector is configured if NetFlow configuration is presented
if 'server' not in flow_config['netflow']:
raise ConfigError('You need to configure at least one NetFlow server!')
# Check if configured netflow source-address exist in the system
if 'source_address' in flow_config['netflow']:
- if not is_addr_assigned(flow_config['netflow']['source_address']):
+ if not is_addr_assigned(flow_config['netflow']['source_address'], netflow_vrf):
tmp = flow_config['netflow']['source_address']
raise ConfigError(f'Configured "netflow source-address {tmp}" does not exist on the system!')
diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py
index e18b426b1..626a3757e 100755
--- a/src/conf_mode/high-availability.py
+++ b/src/conf_mode/high-availability.py
@@ -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 os
from sys import exit
from ipaddress import ip_interface
@@ -23,14 +22,11 @@ from ipaddress import IPv6Interface
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.ifconfig.vrrp import VRRP
from vyos.template import render
from vyos.template import is_ipv4
from vyos.template import is_ipv6
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -42,42 +38,12 @@ def get_config(config=None):
conf = Config()
base = ['high-availability']
- base_vrrp = ['high-availability', 'vrrp']
if not conf.exists(base):
return None
ha = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- if 'vrrp' in ha:
- if dict_search('vrrp.global_parameters.garp', ha) != None:
- default_values = defaults(base_vrrp + ['global-parameters', 'garp'])
- ha['vrrp']['global_parameters']['garp'] = dict_merge(
- default_values, ha['vrrp']['global_parameters']['garp'])
-
- if 'group' in ha['vrrp']:
- default_values = defaults(base_vrrp + ['group'])
- default_values_garp = defaults(base_vrrp + ['group', 'garp'])
-
- # 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 'garp' in default_values:
- del default_values['garp']
- for group in ha['vrrp']['group']:
- ha['vrrp']['group'][group] = dict_merge(default_values, ha['vrrp']['group'][group])
-
- # 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 'garp' in ha['vrrp']['group'][group]:
- ha['vrrp']['group'][group]['garp'] = dict_merge(
- default_values_garp, ha['vrrp']['group'][group]['garp'])
-
- # Merge per virtual-server default values
- if 'virtual_server' in ha:
- default_values = defaults(base + ['virtual-server'])
- for vs in ha['virtual_server']:
- ha['virtual_server'][vs] = dict_merge(default_values, ha['virtual_server'][vs])
+ no_tag_node_value_mangle=True,
+ get_first_key=True, with_defaults=True)
## Get the sync group used for conntrack-sync
conntrack_path = ['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group']
@@ -112,7 +78,7 @@ def verify(ha):
from vyos.utils.dict import check_mutually_exclusive_options
try:
check_mutually_exclusive_options(group_config["health_check"], health_check_types, required=True)
- except ValueError as e:
+ except ValueError:
Warning(f'Health check configuration for VRRP group "{group}" will remain unused ' \
f'until it has one of the following options: {health_check_types}')
# XXX: health check has default options so we need to remove it
@@ -175,6 +141,11 @@ def verify(ha):
# Virtual-server
if 'virtual_server' in ha:
for vs, vs_config in ha['virtual_server'].items():
+
+ if 'address' not in vs_config and 'fwmark' not in vs_config:
+ raise ConfigError('Either address or fwmark is required '
+ f'but not set for virtual-server "{vs}"')
+
if 'port' not in vs_config and 'fwmark' not in vs_config:
raise ConfigError(f'Port or fwmark is required but not set for virtual-server "{vs}"')
if 'port' in vs_config and 'fwmark' in vs_config:
diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py
index 93f244f42..36d1f6493 100755
--- a/src/conf_mode/host_name.py
+++ b/src/conf_mode/host_name.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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,16 +18,15 @@ import re
import sys
import copy
-import vyos.util
import vyos.hostsd_client
from vyos.base import Warning
from vyos.config import Config
from vyos.ifconfig import Section
from vyos.template import is_ip
-from vyos.util import cmd
-from vyos.util import call
-from vyos.util import process_named_running
+from vyos.utils.process import cmd
+from vyos.utils.process import call
+from vyos.utils.process import process_named_running
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index 7e801eb26..793a90d88 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -24,12 +24,9 @@ from copy import deepcopy
import vyos.defaults
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configdep import set_dependents, call_dependents
from vyos.template import render
-from vyos.util import cmd
-from vyos.util import call
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -72,8 +69,9 @@ def get_config(config=None):
return None
api_dict = conf.get_config_dict(base, key_mangling=('-', '_'),
- no_tag_node_value_mangle=True,
- get_first_key=True)
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
# One needs to 'flatten' the keys dict from the config into the
# http-api.conf format for api_keys:
@@ -93,8 +91,8 @@ def get_config(config=None):
if 'api_keys' in api_dict:
keys_added = True
- if 'graphql' in api_dict:
- api_dict = dict_merge(defaults(base), api_dict)
+ if api_dict.from_defaults(['graphql']):
+ del api_dict['graphql']
http_api.update(api_dict)
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index b0c38e8d3..010490c7e 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -28,10 +28,10 @@ from vyos import ConfigError
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
-from vyos.util import call
-from vyos.util import check_port_availability
-from vyos.util import is_listen_port_bind_service
-from vyos.util import write_file
+from vyos.utils.process import call
+from vyos.utils.network import check_port_availability
+from vyos.utils.network import is_listen_port_bind_service
+from vyos.utils.file import write_file
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py
index de6a51c64..40db417dd 100755
--- a/src/conf_mode/igmp_proxy.py
+++ b/src/conf_mode/igmp_proxy.py
@@ -21,11 +21,9 @@ from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -39,16 +37,9 @@ def get_config(config=None):
conf = Config()
base = ['protocols', 'igmp-proxy']
- igmp_proxy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
- if 'interface' in igmp_proxy:
- # T2665: we must add the tagNode defaults individually until this is
- # moved to the base class
- default_values = defaults(base + ['interface'])
- for interface in igmp_proxy['interface']:
- igmp_proxy['interface'][interface] = dict_merge(default_values,
- igmp_proxy['interface'][interface])
-
+ igmp_proxy = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_defaults=True)
if conf.exists(['protocols', 'igmp']):
igmp_proxy.update({'igmp_configured': ''})
diff --git a/src/conf_mode/intel_qat.py b/src/conf_mode/intel_qat.py
index dd04a002d..e4b248675 100755
--- a/src/conf_mode/intel_qat.py
+++ b/src/conf_mode/intel_qat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,8 @@ import re
from sys import exit
from vyos.config import Config
-from vyos.util import popen, run
+from vyos.utils.process import popen
+from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 9936620c8..0bd306ed0 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -35,9 +35,9 @@ from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ifconfig import BondIf
from vyos.ifconfig import Section
-from vyos.util import dict_search
-from vyos.validate import has_address_configured
-from vyos.validate import has_vrf_configured
+from vyos.utils.dict import dict_search
+from vyos.configdict import has_address_configured
+from vyos.configdict import has_vrf_configured
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -195,11 +195,11 @@ def verify(bond):
raise ConfigError(error_msg + 'it does not exist!')
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 4da3b097f..c82f01e53 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -14,10 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-
from sys import exit
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -25,17 +22,14 @@ from vyos.configdict import 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
-from vyos.validate import has_vrf_configured
-from vyos.xml import defaults
+from vyos.configdict import has_address_configured
+from vyos.configdict import has_vrf_configured
-from vyos.util import cmd
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
@@ -61,22 +55,8 @@ def get_config(config=None):
else:
bridge.update({'member' : {'interface_remove' : tmp }})
- if dict_search('member.interface', bridge) != None:
- # XXX: T2665: we need a copy of the dict keys for iteration, else we will get:
- # RuntimeError: dictionary changed size during iteration
+ if dict_search('member.interface', bridge) is not None:
for interface in list(bridge['member']['interface']):
- for key in ['cost', 'priority']:
- if interface == key:
- del bridge['member']['interface'][key]
- continue
-
- # the default dictionary is not properly paged into the dict (see T2665)
- # thus we will ammend it ourself
- default_member_values = defaults(base + ['member', 'interface'])
- for interface,interface_config in bridge['member']['interface'].items():
- bridge['member']['interface'][interface] = dict_merge(
- default_member_values, bridge['member']['interface'][interface])
-
# Check if member interface is already member of another bridge
tmp = is_member(conf, interface, 'bridge')
if tmp and bridge['ifname'] not in tmp:
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 31cfab368..f3e65ad5e 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -40,9 +40,9 @@ 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
-from vyos.util import dict_search
-from vyos.util import write_file
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -145,12 +145,6 @@ def verify(ethernet):
raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\
'for MTU size larger then 1500 bytes')
- # XDP requires multiple TX queues
- if 'xdp' in ethernet:
- queues = glob(f'/sys/class/net/{ifname}/queues/tx-*')
- if len(queues) < 2:
- raise ConfigError('XDP requires additional TX queues, too few available!')
-
if {'is_bond_member', 'mac'} <= set(ethernet):
Warning(f'changing mac address "{mac}" will be ignored as "{ifname}" ' \
f'is a member of bond "{is_bond_member}"'.format(**ethernet))
@@ -192,14 +186,15 @@ def generate(ethernet):
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'][ca_cert_name]
+ ca_chains = []
- loaded_ca_cert = load_certificate(pki_ca_cert['certificate'])
- ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
+ for ca_cert_name in ethernet['eapol']['ca_certificate']:
+ 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)
+ ca_chains.append('\n'.join(encode_certificate(c) for c in ca_full_chain))
- write_file(ca_cert_file_path,
- '\n'.join(encode_certificate(c) for c in ca_full_chain))
+ write_file(ca_cert_file_path, '\n'.join(ca_chains))
return None
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index ca321e01d..e1db3206e 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -28,8 +28,8 @@ from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import L2TPv3If
-from vyos.util import check_kmod
-from vyos.validate import is_addr_assigned
+from vyos.utils.kernel import check_kmod
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py
index 649ea8d50..0a927ac88 100755
--- a/src/conf_mode/interfaces-macsec.py
+++ b/src/conf_mode/interfaces-macsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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
@@ -33,9 +33,9 @@ from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import MACsecIf
from vyos.ifconfig import Interface
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import is_systemd_service_running
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -43,6 +43,14 @@ airbag.enable()
# XXX: wpa_supplicant works on the source interface
wpa_suppl_conf = '/run/wpa_supplicant/{source_interface}.conf'
+# Constants
+## gcm-aes-128 requires a 128bit long key - 32 characters (string) = 16byte = 128bit
+GCM_AES_128_LEN: int = 32
+GCM_128_KEY_ERROR = 'gcm-aes-128 requires a 128bit long key!'
+## gcm-aes-256 requires a 256bit long key - 64 characters (string) = 32byte = 256bit
+GCM_AES_256_LEN: int = 64
+GCM_256_KEY_ERROR = 'gcm-aes-256 requires a 256bit long key!'
+
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -89,18 +97,54 @@ def verify(macsec):
raise ConfigError('Cipher suite must be set for MACsec "{ifname}"'.format(**macsec))
if dict_search('security.encrypt', macsec) != None:
- if dict_search('security.mka.cak', macsec) == None or dict_search('security.mka.ckn', macsec) == None:
- raise ConfigError('Missing mandatory MACsec security keys as encryption is enabled!')
+ # Check that only static or MKA config is present
+ if dict_search('security.static', macsec) != None and (dict_search('security.mka.cak', macsec) != None or dict_search('security.mka.ckn', macsec) != None):
+ raise ConfigError('Only static or MKA can be used!')
+
+ # Logic to check static configuration
+ if dict_search('security.static', macsec) != None:
+ # tx-key must be defined
+ if dict_search('security.static.key', macsec) == None:
+ raise ConfigError('Static MACsec tx-key must be defined.')
+
+ tx_len = len(dict_search('security.static.key', macsec))
+
+ if dict_search('security.cipher', macsec) == 'gcm-aes-128' and tx_len != GCM_AES_128_LEN:
+ raise ConfigError(GCM_128_KEY_ERROR)
+
+ if dict_search('security.cipher', macsec) == 'gcm-aes-256' and tx_len != GCM_AES_256_LEN:
+ raise ConfigError(GCM_256_KEY_ERROR)
+
+ # Make sure at least one peer is defined
+ if 'peer' not in macsec['security']['static']:
+ raise ConfigError('Must have at least one peer defined for static MACsec')
+
+ # For every enabled peer, make sure a MAC and rx-key is defined
+ for peer, peer_config in macsec['security']['static']['peer'].items():
+ if 'disable' not in peer_config and ('mac' not in peer_config or 'key' not in peer_config):
+ raise ConfigError('Every enabled MACsec static peer must have a MAC address and rx-key defined.')
+
+ # check rx-key length against cipher suite
+ rx_len = len(peer_config['key'])
+
+ if dict_search('security.cipher', macsec) == 'gcm-aes-128' and rx_len != GCM_AES_128_LEN:
+ raise ConfigError(GCM_128_KEY_ERROR)
+
+ if dict_search('security.cipher', macsec) == 'gcm-aes-256' and rx_len != GCM_AES_256_LEN:
+ raise ConfigError(GCM_256_KEY_ERROR)
+
+ # Logic to check MKA configuration
+ else:
+ if dict_search('security.mka.cak', macsec) == None or dict_search('security.mka.ckn', macsec) == None:
+ raise ConfigError('Missing mandatory MACsec security keys as encryption is enabled!')
- cak_len = len(dict_search('security.mka.cak', macsec))
+ cak_len = len(dict_search('security.mka.cak', macsec))
- if dict_search('security.cipher', macsec) == 'gcm-aes-128' and cak_len != 32:
- # gcm-aes-128 requires a 128bit long key - 32 characters (string) = 16byte = 128bit
- raise ConfigError('gcm-aes-128 requires a 128bit long key!')
+ if dict_search('security.cipher', macsec) == 'gcm-aes-128' and cak_len != GCM_AES_128_LEN:
+ raise ConfigError(GCM_128_KEY_ERROR)
- elif dict_search('security.cipher', macsec) == 'gcm-aes-256' and cak_len != 64:
- # gcm-aes-128 requires a 128bit long key - 64 characters (string) = 32byte = 256bit
- raise ConfigError('gcm-aes-128 requires a 256bit long key!')
+ elif dict_search('security.cipher', macsec) == 'gcm-aes-256' and cak_len != GCM_AES_256_LEN:
+ raise ConfigError(GCM_256_KEY_ERROR)
if 'source_interface' in macsec:
# MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad
@@ -115,7 +159,9 @@ def verify(macsec):
def generate(macsec):
- render(wpa_suppl_conf.format(**macsec), 'macsec/wpa_supplicant.conf.j2', macsec)
+ # Only generate wpa_supplicant config if using MKA
+ if dict_search('security.mka.cak', macsec):
+ render(wpa_suppl_conf.format(**macsec), 'macsec/wpa_supplicant.conf.j2', macsec)
return None
@@ -142,8 +188,10 @@ def apply(macsec):
i = MACsecIf(**macsec)
i.update(macsec)
- if not is_systemd_service_running(systemd_service) or 'shutdown_required' in macsec:
- call(f'systemctl reload-or-restart {systemd_service}')
+ # Only reload/restart if using MKA
+ if dict_search('security.mka.cak', macsec):
+ if not is_systemd_service_running(systemd_service) or 'shutdown_required' in macsec:
+ call(f'systemctl reload-or-restart {systemd_service}')
return None
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 6f227b0d1..1d0feb56f 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -50,16 +50,18 @@ from vyos.pki import wrap_private_key
from vyos.template import render
from vyos.template import is_ipv4
from vyos.template import is_ipv6
-from vyos.util import call
-from vyos.util import chown
-from vyos.util import cmd
-from vyos.util import dict_search
-from vyos.util import dict_search_args
-from vyos.util import is_list_equal
-from vyos.util import makedir
-from vyos.util import read_file
-from vyos.util import write_file
-from vyos.validate import is_addr_assigned
+from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_args
+from vyos.utils.list import is_list_equal
+from vyos.utils.file import makedir
+from vyos.utils.file import read_file
+from vyos.utils.file import write_file
+from vyos.utils.kernel import check_kmod
+from vyos.utils.kernel import unload_kmod
+from vyos.utils.process import call
+from vyos.utils.permission import chown
+from vyos.utils.process import cmd
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
@@ -86,30 +88,45 @@ def get_config(config=None):
conf = Config()
base = ['interfaces', 'openvpn']
- tmp_pki = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
ifname, openvpn = get_interface_dict(conf, base)
-
- if 'deleted' not in openvpn:
- openvpn['pki'] = tmp_pki
- if is_node_changed(conf, base + [ifname, 'openvpn-option']):
- openvpn.update({'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)
-
- # We have to cleanup the config dict, as default values could enable features
- # which are not explicitly enabled on the CLI. Example: server mfa totp
- # originate comes with defaults, which will enable the
- # totp plugin, even when not set via CLI so we
- # need to check this first and drop those keys
- if dict_search('server.mfa.totp', tmp) == None:
- del openvpn['server']['mfa']
-
openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn)
+ if 'deleted' in openvpn:
+ return openvpn
+
+ openvpn['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ if is_node_changed(conf, base + [ifname, 'openvpn-option']):
+ openvpn.update({'restart_required': {}})
+ if is_node_changed(conf, base + [ifname, 'enable-dco']):
+ openvpn.update({'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)
+
+ # We have to cleanup the config dict, as default values could enable features
+ # which are not explicitly enabled on the CLI. Example: server mfa totp
+ # originate comes with defaults, which will enable the
+ # totp plugin, even when not set via CLI so we
+ # need to check this first and drop those keys
+ if dict_search('server.mfa.totp', tmp) == None:
+ del openvpn['server']['mfa']
+
+ # OpenVPN Data-Channel-Offload (DCO) is a Kernel module. If loaded it applies to all
+ # OpenVPN interfaces. Check if DCO is used by any other interface instance.
+ tmp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ for interface, interface_config in tmp.items():
+ # If one interface has DCO configured, enable it. No need to further check
+ # all other OpenVPN interfaces. We must use a dedicated key to indicate
+ # the Kernel module must be loaded or not. The per interface "offload.dco"
+ # key is required per OpenVPN interface instance.
+ if dict_search('offload.dco', interface_config) != None:
+ openvpn['module_load_dco'] = {}
+ break
+
return openvpn
def is_ec_private_key(pki, cert_name):
@@ -149,17 +166,23 @@ def verify_pki(openvpn):
raise ConfigError(f'Invalid shared-secret on openvpn interface {interface}')
if tls:
- if 'ca_certificate' not in tls:
- raise ConfigError(f'Must specify "tls ca-certificate" on openvpn interface {interface}')
+ if (mode in ['server', 'client']) and ('ca_certificate' not in tls):
+ raise ConfigError(f'Must specify "tls ca-certificate" on openvpn interface {interface},\
+ it is required in server and client modes')
+ else:
+ if ('ca_certificate' not in tls) and ('peer_fingerprint' not in tls):
+ raise ConfigError('Either "tls ca-certificate" or "tls peer-fingerprint" is required\
+ on openvpn interface {interface} in site-to-site mode')
- for ca_name in tls['ca_certificate']:
- if ca_name not in pki['ca']:
- raise ConfigError(f'Invalid CA certificate on openvpn interface {interface}')
+ if 'ca_certificate' in tls:
+ for ca_name in tls['ca_certificate']:
+ if ca_name not in pki['ca']:
+ raise ConfigError(f'Invalid CA certificate on openvpn interface {interface}')
- if len(tls['ca_certificate']) > 1:
- sorted_chain = sort_ca_chain(tls['ca_certificate'], pki['ca'])
- if not verify_ca_chain(sorted_chain, pki['ca']):
- raise ConfigError(f'CA certificates are not a valid chain')
+ if len(tls['ca_certificate']) > 1:
+ sorted_chain = sort_ca_chain(tls['ca_certificate'], pki['ca'])
+ if not verify_ca_chain(sorted_chain, pki['ca']):
+ raise ConfigError(f'CA certificates are not a valid chain')
if mode != 'client' and 'auth_key' not in tls:
if 'certificate' not in tls:
@@ -172,16 +195,7 @@ def verify_pki(openvpn):
if dict_search_args(pki, 'certificate', tls['certificate'], 'private', 'password_protected') is not None:
raise ConfigError(f'Cannot use encrypted private key on openvpn interface {interface}')
- if mode == 'server' and 'dh_params' not in tls and not is_ec_private_key(pki, tls['certificate']):
- raise ConfigError('Must specify "tls dh-params" when not using EC keys in server mode')
-
if 'dh_params' in tls:
- if 'dh' not in pki:
- raise ConfigError('There are no DH parameters in PKI configuration')
-
- if tls['dh_params'] not in pki['dh']:
- raise ConfigError(f'Invalid dh-params on openvpn interface {interface}')
-
pki_dh = pki['dh'][tls['dh_params']]
dh_params = load_dh_parameters(pki_dh['parameters'])
dh_numbers = dh_params.parameter_numbers()
@@ -190,6 +204,7 @@ def verify_pki(openvpn):
if dh_bits < 2048:
raise ConfigError(f'Minimum DH key-size is 2048 bits')
+
if 'auth_key' in tls or 'crypt_key' in tls:
if not dict_search_args(pki, 'openvpn', 'shared_secret'):
raise ConfigError('There are no openvpn shared-secrets in PKI configuration')
@@ -478,9 +493,6 @@ def verify(openvpn):
if openvpn['protocol'] == 'tcp-active':
raise ConfigError('Cannot specify "tcp-active" when "tls role" is "passive"')
- if not dict_search('tls.dh_params', openvpn):
- raise ConfigError('Must specify "tls dh-params" when "tls role" is "passive"')
-
if 'certificate' in openvpn['tls'] and is_ec_private_key(openvpn['pki'], openvpn['tls']['certificate']):
if 'dh_params' in openvpn['tls']:
print('Warning: using dh-params and EC keys simultaneously will ' \
@@ -670,6 +682,15 @@ def apply(openvpn):
if interface in interfaces():
VTunIf(interface).remove()
+ # dynamically load/unload DCO Kernel extension if requested
+ dco_module = 'ovpn_dco_v2'
+ if 'module_load_dco' in openvpn:
+ check_kmod(dco_module)
+ else:
+ unload_kmod(dco_module)
+
+ # Now bail out early if interface is disabled or got deleted
+ if 'deleted' in openvpn or 'disable' in openvpn:
return None
# verify specified IP address is present on any interface on this system
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index 5f0b76f90..fca91253c 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -32,8 +32,8 @@ 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
-from vyos.util import is_systemd_service_running
+from vyos.utils.process import call
+from vyos.utils.process import is_systemd_service_running
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces-sstpc.py b/src/conf_mode/interfaces-sstpc.py
index b5cc4cf4e..b588910dc 100755
--- a/src/conf_mode/interfaces-sstpc.py
+++ b/src/conf_mode/interfaces-sstpc.py
@@ -27,10 +27,10 @@ from vyos.pki import encode_certificate
from vyos.pki import find_chain
from vyos.pki import load_certificate
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import is_systemd_service_running
-from vyos.util import write_file
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index 0a3726e94..91aed9cc3 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -33,8 +33,8 @@ from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
from vyos.ifconfig import TunnelIf
-from vyos.util import get_interface_config
-from vyos.util import dict_search
+from vyos.utils.network import get_interface_config
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -55,6 +55,9 @@ def get_config(config=None):
tmp = is_node_changed(conf, base + [ifname, 'encapsulation'])
if tmp: tunnel.update({'encapsulation_changed': {}})
+ tmp = is_node_changed(conf, base + [ifname, 'parameters', 'ip', 'key'])
+ if tmp: tunnel.update({'key_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
@@ -197,7 +200,8 @@ def apply(tunnel):
remote = dict_search('linkinfo.info_data.remote', tmp)
if ('deleted' in tunnel or 'encapsulation_changed' in tunnel or encap in
- ['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any']):
+ ['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any'] or
+ 'key_changed' in tunnel):
if interface in interfaces():
tmp = Interface(interface)
tmp.remove()
diff --git a/src/conf_mode/interfaces-vti.py b/src/conf_mode/interfaces-vti.py
index f4b0436af..9871810ae 100755
--- a/src/conf_mode/interfaces-vti.py
+++ b/src/conf_mode/interfaces-vti.py
@@ -21,7 +21,7 @@ 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.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index b1536148c..a3b0867e0 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -87,8 +87,8 @@ def verify(vxlan):
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):
- raise ConfigError('Group, remote or source-address must be configured')
+ if not any(tmp in ['group', 'remote', 'source_address', 'source_interface'] for tmp in vxlan):
+ raise ConfigError('Group, remote, source-address or source-interface must be configured')
if 'vni' not in vxlan and 'external' not in vxlan:
raise ConfigError(
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 762bad94f..122d9589a 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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,12 +27,14 @@ from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import WireGuardIf
-from vyos.util import check_kmod
-from vyos.util import check_port_availability
+from vyos.utils.kernel import check_kmod
+from vyos.utils.network import check_port_availability
+from vyos.utils.network import is_wireguard_key_pair
from vyos import ConfigError
from vyos import airbag
airbag.enable()
+
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -88,7 +90,6 @@ def verify(wireguard):
# run checks on individual configured WireGuard peer
public_keys = []
-
for tmp in wireguard['peer']:
peer = wireguard['peer'][tmp]
@@ -105,6 +106,10 @@ def verify(wireguard):
if peer['public_key'] in public_keys:
raise ConfigError(f'Duplicate public-key defined on peer "{tmp}"')
+ if 'disable' not in peer:
+ if is_wireguard_key_pair(wireguard['private_key'], peer['public_key']):
+ raise ConfigError(f'Peer "{tmp}" has the same public key as the interface "{wireguard["ifname"]}"')
+
public_keys.append(peer['public_key'])
def apply(wireguard):
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index dd798b5a2..02b4a2500 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -25,16 +25,14 @@ from vyos.configdict import get_interface_dict
from vyos.configdict import dict_merge
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.configverify import verify_bond_bridge_member
from vyos.ifconfig import WiFiIf
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -42,6 +40,8 @@ airbag.enable()
# XXX: wpa_supplicant works on the source interface
wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf'
hostapd_conf = '/run/hostapd/{ifname}.conf'
+hostapd_accept_station_conf = '/run/hostapd/{ifname}_station_accept.conf'
+hostapd_deny_station_conf = '/run/hostapd/{ifname}_station_deny.conf'
def find_other_stations(conf, base, ifname):
"""
@@ -79,30 +79,14 @@ def get_config(config=None):
ifname, wifi = get_interface_dict(conf, base)
- # Cleanup "delete" default values when required user selectable values are
- # not defined at all
- tmp = conf.get_config_dict(base + [ifname], key_mangling=('-', '_'),
- get_first_key=True)
- if not (dict_search('security.wpa.passphrase', tmp) or
- dict_search('security.wpa.radius', tmp)):
- if 'deleted' not in wifi:
+ if 'deleted' not in wifi:
+ # then get_interface_dict provides default keys
+ if wifi.from_defaults(['security', 'wep']): # if not set by user
+ del wifi['security']['wep']
+ if wifi.from_defaults(['security', 'wpa']): # if not set by user
del wifi['security']['wpa']
- # if 'security' key is empty, drop it too
- if len(wifi['security']) == 0:
- del wifi['security']
-
- # defaults include RADIUS server specifics per TAG node which need to be
- # added to individual RADIUS servers instead - so we can simply delete them
- if dict_search('security.wpa.radius.server.port', wifi) != None:
- del wifi['security']['wpa']['radius']['server']['port']
- if not len(wifi['security']['wpa']['radius']['server']):
- del wifi['security']['wpa']['radius']
- if not len(wifi['security']['wpa']):
- del wifi['security']['wpa']
- if not len(wifi['security']):
- del wifi['security']
- if 'security' in wifi and 'wpa' in wifi['security']:
+ if dict_search('security.wpa', wifi) != None:
wpa_cipher = wifi['security']['wpa'].get('cipher')
wpa_mode = wifi['security']['wpa'].get('mode')
if not wpa_cipher:
@@ -120,13 +104,9 @@ def get_config(config=None):
tmp = find_other_stations(conf, base, wifi['ifname'])
if tmp: wifi['station_interfaces'] = tmp
- # Add individual RADIUS server default values
- if dict_search('security.wpa.radius.server', wifi):
- default_values = defaults(base + ['security', 'wpa', 'radius', 'server'])
-
- for server in dict_search('security.wpa.radius.server', wifi):
- wifi['security']['wpa']['radius']['server'][server] = dict_merge(
- default_values, wifi['security']['wpa']['radius']['server'][server])
+ # used in hostapt.conf.j2
+ wifi['hostapd_accept_station_conf'] = hostapd_accept_station_conf.format(**wifi)
+ wifi['hostapd_deny_station_conf'] = hostapd_deny_station_conf.format(**wifi)
return wifi
@@ -142,7 +122,7 @@ def verify(wifi):
raise ConfigError('You must specify a WiFi mode')
if 'ssid' not in wifi and wifi['type'] != 'monitor':
- raise ConfigError('SSID must be configured')
+ raise ConfigError('SSID must be configured unless type is set to "monitor"!')
if wifi['type'] == 'access-point':
if 'country_code' not in wifi:
@@ -215,7 +195,10 @@ def generate(wifi):
if 'deleted' in wifi:
if os.path.isfile(hostapd_conf.format(**wifi)):
os.unlink(hostapd_conf.format(**wifi))
-
+ if os.path.isfile(hostapd_accept_station_conf.format(**wifi)):
+ os.unlink(hostapd_accept_station_conf.format(**wifi))
+ if os.path.isfile(hostapd_deny_station_conf.format(**wifi)):
+ os.unlink(hostapd_deny_station_conf.format(**wifi))
if os.path.isfile(wpa_suppl_conf.format(**wifi)):
os.unlink(wpa_suppl_conf.format(**wifi))
@@ -250,12 +233,12 @@ def generate(wifi):
# render appropriate new config files depending on access-point or station mode
if wifi['type'] == 'access-point':
- render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.j2',
- wifi)
+ render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.j2', wifi)
+ render(hostapd_accept_station_conf.format(**wifi), 'wifi/hostapd_accept_station.conf.j2', wifi)
+ render(hostapd_deny_station_conf.format(**wifi), 'wifi/hostapd_deny_station.conf.j2', wifi)
elif wifi['type'] == 'station':
- render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.j2',
- wifi)
+ render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.j2', wifi)
return None
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index 9ca495476..2515dc838 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -27,12 +27,12 @@ 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
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import DEVNULL
-from vyos.util import is_systemd_service_active
-from vyos.util import write_file
+from vyos.utils.dict import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.process import call
+from vyos.utils.process import DEVNULL
+from vyos.utils.process import is_systemd_service_active
+from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -75,7 +75,6 @@ def get_config(config=None):
# We need to know the amount of other WWAN interfaces as ModemManager needs
# to be started or stopped.
- conf.set_level(base)
wwan['other_interfaces'] = conf.get_config_dict([], key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True)
diff --git a/src/conf_mode/le_cert.py b/src/conf_mode/le_cert.py
index 6e169a3d5..06c7e7b72 100755
--- a/src/conf_mode/le_cert.py
+++ b/src/conf_mode/le_cert.py
@@ -20,9 +20,9 @@ import os
import vyos.defaults
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import cmd
-from vyos.util import call
-from vyos.util import is_systemd_service_running
+from vyos.utils.process import cmd
+from vyos.utils.process import call
+from vyos.utils.process import is_systemd_service_running
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py
index c703c1fe0..c2e87d171 100755
--- a/src/conf_mode/lldp.py
+++ b/src/conf_mode/lldp.py
@@ -20,13 +20,11 @@ from sys import exit
from vyos.base import Warning
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.utils.network import is_addr_assigned
+from vyos.utils.network import is_loopback_addr
from vyos.version import get_version_data
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
from vyos.template import render
from vyos import ConfigError
from vyos import airbag
@@ -46,7 +44,9 @@ def get_config(config=None):
return {}
lldp = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
if conf.exists(['service', 'snmp']):
lldp['system_snmp_enabled'] = ''
@@ -54,27 +54,12 @@ def get_config(config=None):
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])
+ # prune location information if not set by user
+ for interface in lldp.get('interface', []):
+ if lldp.from_defaults(['interface', interface, 'location']):
+ del lldp['interface'][interface]['location']
+ elif lldp.from_defaults(['interface', interface, 'location','coordinate_based']):
+ del lldp['interface'][interface]['location']['coordinate_based']
return lldp
diff --git a/src/conf_mode/load-balancing-haproxy.py b/src/conf_mode/load-balancing-haproxy.py
index b29fdffc7..8fe429653 100755
--- a/src/conf_mode/load-balancing-haproxy.py
+++ b/src/conf_mode/load-balancing-haproxy.py
@@ -20,14 +20,12 @@ from sys import exit
from shutil import rmtree
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.util import call
-from vyos.util import check_port_availability
-from vyos.util import is_listen_port_bind_service
+from vyos.utils.process import call
+from vyos.utils.network import check_port_availability
+from vyos.utils.network import is_listen_port_bind_service
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -54,18 +52,8 @@ def get_config(config=None):
lb['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base)
- if 'backend' in default_values:
- del default_values['backend']
if lb:
- lb = dict_merge(default_values, lb)
-
- if 'backend' in lb:
- for backend in lb['backend']:
- default_balues_backend = defaults(base + ['backend'])
- lb['backend'][backend] = dict_merge(default_balues_backend, lb['backend'][backend])
+ lb = conf.merge_defaults(lb, recursive=True)
return lb
diff --git a/src/conf_mode/load-balancing-wan.py b/src/conf_mode/load-balancing-wan.py
index 7086aaf8b..ad9c80d72 100755
--- a/src/conf_mode/load-balancing-wan.py
+++ b/src/conf_mode/load-balancing-wan.py
@@ -21,10 +21,8 @@ from shutil import rmtree
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.util import cmd
+from vyos.utils.process import cmd
from vyos.template import render
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -41,48 +39,15 @@ def get_config(config=None):
conf = Config()
base = ['load-balancing', 'wan']
- lb = conf.get_config_dict(base,
+ lb = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
get_first_key=True,
- key_mangling=('-', '_'),
- no_tag_node_value_mangle=True)
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base)
- # lb base default values can not be merged here - remove and add them later
- if 'interface_health' in default_values:
- del default_values['interface_health']
- if 'rule' in default_values:
- del default_values['rule']
- lb = dict_merge(default_values, lb)
-
- if 'interface_health' in lb:
- for iface in lb.get('interface_health'):
- default_values_iface = defaults(base + ['interface-health'])
- if 'test' in default_values_iface:
- del default_values_iface['test']
- lb['interface_health'][iface] = dict_merge(
- default_values_iface, lb['interface_health'][iface])
- if 'test' in lb['interface_health'][iface]:
- for node_test in lb['interface_health'][iface]['test']:
- default_values_test = defaults(base +
- ['interface-health', 'test'])
- lb['interface_health'][iface]['test'][node_test] = dict_merge(
- default_values_test,
- lb['interface_health'][iface]['test'][node_test])
-
- if 'rule' in lb:
- for rule in lb.get('rule'):
- default_values_rule = defaults(base + ['rule'])
- if 'interface' in default_values_rule:
- del default_values_rule['interface']
- lb['rule'][rule] = dict_merge(default_values_rule, lb['rule'][rule])
- if not conf.exists(base + ['rule', rule, 'limit']):
- del lb['rule'][rule]['limit']
- if 'interface' in lb['rule'][rule]:
- for iface in lb['rule'][rule]['interface']:
- default_values_rule_iface = defaults(base + ['rule', 'interface'])
- lb['rule'][rule]['interface'][iface] = dict_merge(default_values_rule_iface, lb['rule'][rule]['interface'][iface])
+ with_recursive_defaults=True)
+
+ # prune limit key if not set by user
+ for rule in lb.get('rule', []):
+ if lb.from_defaults(['rule', rule, 'limit']):
+ del lb['rule'][rule]['limit']
return lb
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 9f8221514..9da7fbe80 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,16 +25,14 @@ from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
-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.util import dict_search_args
-from vyos.validate import is_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.kernel import check_kmod
+from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_args
+from vyos.utils.process import cmd
+from vyos.utils.process import run
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
@@ -72,6 +70,7 @@ def verify_rule(config, err_msg, groups_dict):
""" Common verify steps used for both source and destination NAT """
if (dict_search('translation.port', config) != None or
+ dict_search('translation.redirect.port', config) != None or
dict_search('destination.port', config) != None or
dict_search('source.port', config)):
@@ -125,6 +124,18 @@ def verify_rule(config, err_msg, groups_dict):
if config['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port-group')
+ if 'load_balance' in config:
+ for item in ['source-port', 'destination-port']:
+ if item in config['load_balance']['hash'] and config['protocol'] not in ['tcp', 'udp']:
+ raise ConfigError('Protocol must be tcp or udp when specifying hash ports')
+ count = 0
+ if 'backend' in config['load_balance']:
+ for member in config['load_balance']['backend']:
+ weight = config['load_balance']['backend'][member]['weight']
+ count = count + int(weight)
+ if count != 100:
+ Warning(f'Sum of weight for nat load balance rule is not 100. You may get unexpected behaviour')
+
def get_config(config=None):
if config:
conf = config
@@ -132,16 +143,9 @@ def get_config(config=None):
conf = Config()
base = ['nat']
- nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
- # T2665: we must add the tagNode defaults individually until this is
- # moved to the base class
- for direction in ['source', 'destination', 'static']:
- if direction in nat:
- default_values = defaults(base + [direction, 'rule'])
- for rule in dict_search(f'{direction}.rule', nat) or []:
- nat[direction]['rule'][rule] = dict_merge(default_values,
- nat[direction]['rule'][rule])
+ nat = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
# read in current nftable (once) for further processing
tmp = cmd('nft -j list table raw')
@@ -198,7 +202,7 @@ def verify(nat):
Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
if not dict_search('translation.address', config) and not dict_search('translation.port', config):
- if 'exclude' not in config:
+ if 'exclude' not in config and 'backend' not in config['load_balance']:
raise ConfigError(f'{err_msg} translation requires address and/or port')
addr = dict_search('translation.address', config)
@@ -210,7 +214,6 @@ def verify(nat):
# common rule verification
verify_rule(config, err_msg, nat['firewall_group'])
-
if dict_search('destination.rule', nat):
for rule, config in dict_search('destination.rule', nat).items():
err_msg = f'Destination NAT configuration error in rule {rule}:'
@@ -221,8 +224,8 @@ def verify(nat):
elif config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces():
Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
- if not dict_search('translation.address', config) and not dict_search('translation.port', config):
- if 'exclude' not in config:
+ if not dict_search('translation.address', config) and not dict_search('translation.port', config) and 'redirect' not in config['translation']:
+ if 'exclude' not in config and 'backend' not in config['load_balance']:
raise ConfigError(f'{err_msg} translation requires address and/or port')
# common rule verification
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index d8f913b0c..4c12618bc 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -23,13 +23,11 @@ from netifaces import interfaces
from vyos.base import Warning
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 check_kmod
-from vyos.util import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.kernel import check_kmod
+from vyos.utils.dict import dict_search
from vyos.template import is_ipv6
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -60,16 +58,6 @@ def get_config(config=None):
base = ['nat66']
nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # T2665: we must add the tagNode defaults individually until this is
- # moved to the base class
- for direction in ['source', 'destination']:
- if direction in nat:
- default_values = defaults(base + [direction, 'rule'])
- if 'rule' in nat[direction]:
- for rule in nat[direction]['rule']:
- nat[direction]['rule'][rule] = dict_merge(default_values,
- nat[direction]['rule'][rule])
-
# read in current nftable (once) for further processing
tmp = cmd('nft -j list table ip6 raw')
nftable_json = json.loads(tmp)
diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py
index 20129ce65..95ab83dbc 100755
--- a/src/conf_mode/netns.py
+++ b/src/conf_mode/netns.py
@@ -22,9 +22,9 @@ from tempfile import NamedTemporaryFile
from vyos.config import Config
from vyos.configdict import node_changed
from vyos.ifconfig import Interface
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import get_interface_config
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py
index 92cb73aab..1cc23a7df 100755
--- a/src/conf_mode/ntp.py
+++ b/src/conf_mode/ntp.py
@@ -20,10 +20,11 @@ from vyos.config import Config
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_interface_exists
-from vyos.util import call
-from vyos.util import chmod_750
-from vyos.util import get_interface_config
+from vyos.utils.process import call
+from vyos.utils.permission import chmod_750
+from vyos.utils.network import get_interface_config
from vyos.template import render
+from vyos.template import is_ipv4
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -62,16 +63,29 @@ def verify(ntp):
if 'interface' in ntp:
# If ntpd should listen on a given interface, ensure it exists
- for interface in ntp['interface']:
- verify_interface_exists(interface)
-
- # If we run in a VRF, our interface must belong to this VRF, too
- if 'vrf' in ntp:
- tmp = get_interface_config(interface)
- vrf_name = ntp['vrf']
- if 'master' not in tmp or tmp['master'] != vrf_name:
- raise ConfigError(f'NTP runs in VRF "{vrf_name}" - "{interface}" '\
- f'does not belong to this VRF!')
+ interface = ntp['interface']
+ verify_interface_exists(interface)
+
+ # If we run in a VRF, our interface must belong to this VRF, too
+ if 'vrf' in ntp:
+ tmp = get_interface_config(interface)
+ vrf_name = ntp['vrf']
+ if 'master' not in tmp or tmp['master'] != vrf_name:
+ raise ConfigError(f'NTP runs in VRF "{vrf_name}" - "{interface}" '\
+ f'does not belong to this VRF!')
+
+ if 'listen_address' in ntp:
+ ipv4_addresses = 0
+ ipv6_addresses = 0
+ for address in ntp['listen_address']:
+ if is_ipv4(address):
+ ipv4_addresses += 1
+ else:
+ ipv6_addresses += 1
+ if ipv4_addresses > 1:
+ raise ConfigError(f'NTP Only admits one ipv4 value for listen-address parameter ')
+ if ipv6_addresses > 1:
+ raise ConfigError(f'NTP Only admits one ipv6 value for listen-address parameter ')
return None
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index 54de467ca..34ba2fe69 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -18,7 +18,6 @@ from sys import exit
from vyos.config import Config
from vyos.configdep import set_dependents, call_dependents
-from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.pki import is_ca_certificate
from vyos.pki import load_certificate
@@ -26,9 +25,8 @@ from vyos.pki import load_public_key
from vyos.pki import load_private_key
from vyos.pki import load_crl
from vyos.pki import load_dh_parameters
-from vyos.util import dict_search_args
-from vyos.util import dict_search_recursive
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search_args
+from vyos.utils.dict import dict_search_recursive
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -113,8 +111,7 @@ def get_config(config=None):
# We only merge on the defaults of there is a configuration at all
if conf.exists(base):
- default_values = defaults(base)
- pki = dict_merge(default_values, pki)
+ pki = conf.merge_defaults(pki, recursive=True)
# We need to get the entire system configuration to verify that we are not
# deleting a certificate that is still referenced somewhere!
diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py
index 3f834f55c..79526f82a 100755
--- a/src/conf_mode/policy-local-route.py
+++ b/src/conf_mode/policy-local-route.py
@@ -24,7 +24,7 @@ from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configdict import leaf_node_changed
from vyos.template import render
-from vyos.util import call
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/policy-route.py b/src/conf_mode/policy-route.py
index 40a32efb3..adad012de 100755
--- a/src/conf_mode/policy-route.py
+++ b/src/conf_mode/policy-route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,9 +22,9 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
from vyos.template import render
-from vyos.util import cmd
-from vyos.util import dict_search_args
-from vyos.util import run
+from vyos.utils.dict import dict_search_args
+from vyos.utils.process import cmd
+from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index 331194fec..4df893ebf 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -19,7 +19,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.template import render_to_string
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import frr
from vyos import airbag
diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py
index 20821c7f2..104711b55 100755
--- a/src/conf_mode/protocols_babel.py
+++ b/src/conf_mode/protocols_babel.py
@@ -19,13 +19,13 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.config import config_dict_merge
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
from vyos.template import render_to_string
from vyos import ConfigError
from vyos import frr
@@ -38,7 +38,8 @@ def get_config(config=None):
else:
conf = Config()
base = ['protocols', 'babel']
- babel = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ babel = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True)
# FRR has VRF support for different routing daemons. As interfaces belong
# to VRFs - or the global VRF, we need to check for changed interfaces so
@@ -54,15 +55,13 @@ def get_config(config=None):
return babel
# 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)
+ # values which we need to update into the dictionary retrieved.
+ default_values = conf.get_config_defaults(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ recursive=True)
- # XXX: T2665: we currently have no nice way for defaults under tag nodes,
- # clean them out and add them manually :(
- del default_values['interface']
-
- # merge in remaining default values
- babel = dict_merge(default_values, babel)
+ # merge in default values
+ babel = config_dict_merge(default_values, babel)
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 0436abaf9..dab784662 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -17,12 +17,10 @@
import os
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configverify import verify_vrf
from vyos.template import is_ipv6
from vyos.template import render_to_string
-from vyos.validate import is_ipv6_link_local
-from vyos.xml import defaults
+from vyos.utils.network import is_ipv6_link_local
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -41,18 +39,7 @@ def get_config(config=None):
if not conf.exists(base):
return bfd
- # We have gathered the dict representation of the CLI, but there are
- # default options which we need to update into the dictionary retrived.
- # XXX: T2665: we currently have no nice way for defaults under tag
- # nodes, thus we load the defaults "by hand"
- default_values = defaults(base + ['peer'])
- if 'peer' in bfd:
- for peer in bfd['peer']:
- bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer])
-
- if 'profile' in bfd:
- for profile in bfd['profile']:
- bfd['profile'][profile] = dict_merge(default_values, bfd['profile'][profile])
+ bfd = conf.merge_defaults(bfd, recursive=True)
return bfd
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index b23584bdb..00015023c 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-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,16 @@ from sys import argv
from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configdict import node_changed
from vyos.configverify import verify_prefix_list
from vyos.configverify import verify_route_map
from vyos.configverify import verify_vrf
from vyos.template import is_ip
from vyos.template import is_interface
from vyos.template import render_to_string
-from vyos.util import dict_search
-from vyos.validate import is_addr_assigned
+from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_vrf
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -55,6 +57,12 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
+ # Remove per interface MPLS configuration - get a list if changed
+ # nodes under the interface tagNode
+ interfaces_removed = node_changed(conf, base + ['interface'])
+ if interfaces_removed:
+ bgp['interface_removed'] = list(interfaces_removed)
+
# Assign the name of our VRF context. This MUST be done before the return
# statement below, else on deletion we will delete the default instance
# instead of the VRF instance.
@@ -195,14 +203,21 @@ def verify_remote_as(peer_config, bgp_config):
return None
def verify_afi(peer_config, bgp_config):
+ # If address_family configured under neighboor
if 'address_family' in peer_config:
return True
+ # If address_family configured under peer-group
+ # if neighbor interface configured
+ peer_group_name = ''
+ if dict_search('interface.peer_group', peer_config):
+ peer_group_name = peer_config['interface']['peer_group']
+ # if neighbor IP configured.
if 'peer_group' in peer_config:
peer_group_name = peer_config['peer_group']
+ if peer_group_name:
tmp = dict_search(f'peer_group.{peer_group_name}.address_family', bgp_config)
if tmp: return True
-
return False
def verify(bgp):
@@ -231,6 +246,18 @@ def verify(bgp):
if 'system_as' not in bgp:
raise ConfigError('BGP system-as number must be defined!')
+ # Verify vrf on interface and bgp section
+ if 'interface' in bgp:
+ for interface in bgp['interface']:
+ error_msg = f'Interface "{interface}" belongs to different VRF instance'
+ tmp = get_interface_vrf(interface)
+ if 'vrf' in bgp:
+ if bgp['vrf'] != tmp:
+ vrf = bgp['vrf']
+ raise ConfigError(f'{error_msg} "{vrf}"!')
+ elif tmp != 'default':
+ raise ConfigError(f'{error_msg} "{tmp}"!')
+
# Common verification for both peer-group and neighbor statements
for neighbor in ['neighbor', 'peer_group']:
# bail out early if there is no neighbor or peer-group statement
@@ -448,6 +475,8 @@ def verify(bgp):
if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
raise ConfigError(
'Command "import vrf" conflicts with "rd vpn export" command!')
+ if not dict_search('parameters.router_id', bgp):
+ Warning(f'BGP "router-id" is required when using "rd" and "route-target"!')
if dict_search('route_target.vpn.both', afi_config):
if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
@@ -520,6 +549,14 @@ def apply(bgp):
vrf = ' vrf ' + bgp['vrf']
frr_cfg.load_configuration(bgp_daemon)
+
+ # Remove interface specific config
+ for key in ['interface', 'interface_removed']:
+ if key not in bgp:
+ continue
+ for interface in bgp[key]:
+ frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
+
frr_cfg.modify_section(f'^router bgp \d+{vrf}', stop_pattern='^exit', remove_stop_mark=True)
if 'frr_bgpd_config' in bgp:
frr_cfg.add_before(frr.default_add_before, bgp['frr_bgpd_config'])
diff --git a/src/conf_mode/protocols_failover.py b/src/conf_mode/protocols_failover.py
index 85e984afe..e7e44db84 100755
--- a/src/conf_mode/protocols_failover.py
+++ b/src/conf_mode/protocols_failover.py
@@ -19,10 +19,8 @@ import json
from pathlib import Path
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
@@ -42,15 +40,12 @@ def get_config(config=None):
conf = Config()
base = ['protocols', 'failover']
- failover = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ failover = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True)
# Set default values only if we set config
- if failover.get('route'):
- for route, route_config in failover.get('route').items():
- for next_hop, next_hop_config in route_config.get('next_hop').items():
- default_values = defaults(base + ['route'])
- failover['route'][route]['next_hop'][next_hop] = dict_merge(
- default_values['next_hop'], failover['route'][route]['next_hop'][next_hop])
+ if failover.get('route') is not None:
+ failover = conf.merge_defaults(failover, recursive=True)
return failover
diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py
index 65cc2beba..f6097e282 100755
--- a/src/conf_mode/protocols_igmp.py
+++ b/src/conf_mode/protocols_igmp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,7 +21,8 @@ from sys import exit
from vyos import ConfigError
from vyos.config import Config
-from vyos.util import call, process_named_running
+from vyos.utils.process import process_named_running
+from vyos.utils.process import call
from vyos.template import render
from signal import SIGTERM
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index ecca87db0..e00c58ee4 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -25,10 +25,9 @@ from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_interface_exists
from vyos.ifconfig import Interface
-from vyos.util import dict_search
-from vyos.util import get_interface_config
+from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
from vyos.template import render_to_string
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -64,19 +63,14 @@ def get_config(config=None):
if interfaces_removed:
isis['interface_removed'] = list(interfaces_removed)
- # Bail out early if configuration tree does not exist
+ # Bail out early if configuration tree does no longer exist. this must
+ # be done after retrieving the list of interfaces to be removed.
if not conf.exists(base):
isis.update({'deleted' : ''})
return isis
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- # XXX: Note that we can not call defaults(base), as defaults does not work
- # on an instance of a tag node. As we use the exact same CLI definition for
- # both the non-vrf and vrf version this is absolutely safe!
- default_values = defaults(base_path)
# merge in default values
- isis = dict_merge(default_values, isis)
+ isis = conf.merge_defaults(isis, recursive=True)
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
@@ -254,7 +248,7 @@ def apply(isis):
if key not in isis:
continue
for interface in isis[key]:
- frr_cfg.modify_section(f'^interface {interface}{vrf}', stop_pattern='^exit', remove_stop_mark=True)
+ frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
if 'frr_isisd_config' in isis:
frr_cfg.add_before(frr.default_add_before, isis['frr_isisd_config'])
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 73af6595b..177a43444 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -21,9 +21,9 @@ from sys import exit
from glob import glob
from vyos.config import Config
from vyos.template import render_to_string
-from vyos.util import dict_search
-from vyos.util import read_file
-from vyos.util import sysctl_write
+from vyos.utils.dict import dict_search
+from vyos.utils.file import read_file
+from vyos.utils.system import sysctl_write
from vyos.configverify import verify_interface_exists
from vyos import ConfigError
from vyos import frr
diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py
index d28ced4fd..5ec0bc9e5 100755
--- a/src/conf_mode/protocols_nhrp.py
+++ b/src/conf_mode/protocols_nhrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,8 +19,8 @@ import os
from vyos.config import Config
from vyos.configdict import node_changed
from vyos.template import render
-from vyos.util import process_named_running
-from vyos.util import run
+from vyos.utils.process import process_named_running
+from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index b73483470..cddd3765e 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,6 +20,7 @@ from sys import exit
from sys import argv
from vyos.config import Config
+from vyos.config import config_dict_merge
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
@@ -27,9 +28,8 @@ 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
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -65,17 +65,15 @@ def get_config(config=None):
if interfaces_removed:
ospf['interface_removed'] = list(interfaces_removed)
- # Bail out early if configuration tree does not exist
+ # Bail out early if configuration tree does no longer exist. this must
+ # be done after retrieving the list of interfaces to be removed.
if not conf.exists(base):
ospf.update({'deleted' : ''})
return ospf
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
- # XXX: Note that we can not call defaults(base), as defaults does not work
- # on an instance of a tag node. As we use the exact same CLI definition for
- # both the non-vrf and vrf version this is absolutely safe!
- default_values = defaults(base_path)
+ default_values = conf.get_config_defaults(**ospf.kwargs, recursive=True)
# We have to cleanup the default dict, as default values could enable features
# which are not explicitly enabled on the CLI. Example: default-information
@@ -84,60 +82,27 @@ def get_config(config=None):
# need to check this first and probably drop that key.
if dict_search('default_information.originate', ospf) is None:
del default_values['default_information']
- if dict_search('area.area_type.nssa', ospf) is None:
- del default_values['area']['area_type']['nssa']
if 'mpls_te' not in ospf:
del default_values['mpls_te']
+ if 'graceful_restart' not in ospf:
+ del default_values['graceful_restart']
+ for area_num in default_values.get('area', []):
+ if dict_search(f'area.{area_num}.area_type.nssa', ospf) is None:
+ del default_values['area'][area_num]['area_type']['nssa']
- for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static', 'table']:
- # table is a tagNode thus we need to clean out all occurances for the
- # default values and load them in later individually
- if protocol == 'table':
- del default_values['redistribute']['table']
- continue
+ for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static']:
if dict_search(f'redistribute.{protocol}', ospf) is None:
del default_values['redistribute'][protocol]
- # XXX: T2665: we currently have no nice way for defaults under tag nodes,
- # clean them out and add them manually :(
- del default_values['neighbor']
- del default_values['area']['virtual_link']
- del default_values['interface']
-
- # merge in remaining default values
- ospf = dict_merge(default_values, ospf)
+ for interface in ospf.get('interface', []):
+ # We need to reload the defaults on every pass b/c of
+ # hello-multiplier dependency on dead-interval
+ # If hello-multiplier is set, we need to remove the default from
+ # dead-interval.
+ if 'hello_multiplier' in ospf['interface'][interface]:
+ del default_values['interface'][interface]['dead_interval']
- if 'neighbor' in ospf:
- default_values = defaults(base + ['neighbor'])
- for neighbor in ospf['neighbor']:
- ospf['neighbor'][neighbor] = dict_merge(default_values, ospf['neighbor'][neighbor])
-
- if 'area' in ospf:
- default_values = defaults(base + ['area', 'virtual-link'])
- for area, area_config in ospf['area'].items():
- if 'virtual_link' in area_config:
- for virtual_link in area_config['virtual_link']:
- ospf['area'][area]['virtual_link'][virtual_link] = dict_merge(
- default_values, ospf['area'][area]['virtual_link'][virtual_link])
-
- if 'interface' in ospf:
- for interface in ospf['interface']:
- # We need to reload the defaults on every pass b/c of
- # hello-multiplier dependency on dead-interval
- default_values = defaults(base + ['interface'])
- # If hello-multiplier is set, we need to remove the default from
- # dead-interval.
- if 'hello_multiplier' in ospf['interface'][interface]:
- del default_values['dead_interval']
-
- ospf['interface'][interface] = dict_merge(default_values,
- ospf['interface'][interface])
-
- if 'redistribute' in ospf and 'table' in ospf['redistribute']:
- default_values = defaults(base + ['redistribute', 'table'])
- for table in ospf['redistribute']['table']:
- ospf['redistribute']['table'][table] = dict_merge(default_values,
- ospf['redistribute']['table'][table])
+ ospf = config_dict_merge(default_values, ospf)
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
@@ -250,6 +215,13 @@ def verify(ospf):
raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
f'and no-php-flag configured at the same time.')
+ # Check route summarisation
+ if 'summary_address' in ospf:
+ for prefix, prefix_options in ospf['summary_address'].items():
+ if {'tag', 'no_advertise'} <= set(prefix_options):
+ raise ConfigError(f'Can not set both "tag" and "no-advertise" for Type-5 '\
+ f'and Type-7 route summarisation of "{prefix}"!')
+
return None
def generate(ospf):
@@ -278,7 +250,7 @@ def apply(ospf):
if key not in ospf:
continue
for interface in ospf[key]:
- frr_cfg.modify_section(f'^interface {interface}{vrf}', stop_pattern='^exit', remove_stop_mark=True)
+ frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
if 'frr_ospfd_config' in ospf:
frr_cfg.add_before(frr.default_add_before, ospf['frr_ospfd_config'])
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index cb21bd83c..5b1adce30 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -20,6 +20,7 @@ from sys import exit
from sys import argv
from vyos.config import Config
+from vyos.config import config_dict_merge
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
@@ -27,9 +28,8 @@ from vyos.configverify import verify_route_map
from vyos.configverify import verify_interface_exists
from vyos.template import render_to_string
from vyos.ifconfig import Interface
-from vyos.util import dict_search
-from vyos.util import get_interface_config
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -64,17 +64,16 @@ def get_config(config=None):
if interfaces_removed:
ospfv3['interface_removed'] = list(interfaces_removed)
- # Bail out early if configuration tree does not exist
+ # Bail out early if configuration tree does no longer exist. this must
+ # be done after retrieving the list of interfaces to be removed.
if not conf.exists(base):
ospfv3.update({'deleted' : ''})
return ospfv3
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
- # XXX: Note that we can not call defaults(base), as defaults does not work
- # on an instance of a tag node. As we use the exact same CLI definition for
- # both the non-vrf and vrf version this is absolutely safe!
- default_values = defaults(base_path)
+ default_values = conf.get_config_defaults(**ospfv3.kwargs,
+ recursive=True)
# We have to cleanup the default dict, as default values could enable features
# which are not explicitly enabled on the CLI. Example: default-information
@@ -83,13 +82,13 @@ def get_config(config=None):
# need to check this first and probably drop that key.
if dict_search('default_information.originate', ospfv3) is None:
del default_values['default_information']
+ if 'graceful_restart' not in ospfv3:
+ del default_values['graceful_restart']
- # XXX: T2665: we currently have no nice way for defaults under tag nodes,
- # clean them out and add them manually :(
- del default_values['interface']
+ default_values.pop('interface', {})
# merge in remaining default values
- ospfv3 = dict_merge(default_values, ospfv3)
+ ospfv3 = config_dict_merge(default_values, ospfv3)
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
@@ -168,7 +167,7 @@ def apply(ospfv3):
if key not in ospfv3:
continue
for interface in ospfv3[key]:
- frr_cfg.modify_section(f'^interface {interface}{vrf}', stop_pattern='^exit', remove_stop_mark=True)
+ frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
if 'new_frr_config' in ospfv3:
frr_cfg.add_before(frr.default_add_before, ospfv3['new_frr_config'])
diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py
index 78df9b6f8..0aaa0d2c6 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,7 +21,8 @@ from sys import exit
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import call, process_named_running
+from vyos.utils.process import process_named_running
+from vyos.utils.process import call
from vyos.template import render
from signal import SIGTERM
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index c78d90396..bd47dfd00 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -24,8 +24,7 @@ from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
from vyos.template import render_to_string
from vyos import ConfigError
from vyos import frr
@@ -55,9 +54,7 @@ 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)
- # merge in remaining default values
- rip = dict_merge(default_values, rip)
+ rip = conf.merge_defaults(rip, recursive=True)
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py
index 21ff710b3..dd1550033 100755
--- a/src/conf_mode/protocols_ripng.py
+++ b/src/conf_mode/protocols_ripng.py
@@ -23,8 +23,7 @@ from vyos.configdict import dict_merge
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
from vyos.template import render_to_string
from vyos import ConfigError
from vyos import frr
@@ -45,9 +44,7 @@ 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)
- # merge in remaining default values
- ripng = dict_merge(default_values, ripng)
+ ripng = conf.merge_defaults(ripng, recursive=True)
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py
index 62ea9c878..05e876f3b 100755
--- a/src/conf_mode/protocols_rpki.py
+++ b/src/conf_mode/protocols_rpki.py
@@ -19,10 +19,8 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render_to_string
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -43,8 +41,7 @@ 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)
- rpki = dict_merge(default_values, rpki)
+ rpki = conf.merge_defaults(rpki, recursive=True)
return rpki
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index 7b6150696..5def8d645 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -47,7 +47,7 @@ def get_config(config=None):
base_path = ['protocols', 'static']
# eqivalent of the C foo ? 'a' : 'b' statement
base = vrf and ['vrf', 'name', vrf, 'protocols', 'static'] or base_path
- static = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ static = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
# Assign the name of our VRF context
if vrf: static['vrf'] = vrf
diff --git a/src/conf_mode/protocols_static_multicast.py b/src/conf_mode/protocols_static_multicast.py
index 6afdf31f3..7f6ae3680 100755
--- a/src/conf_mode/protocols_static_multicast.py
+++ b/src/conf_mode/protocols_static_multicast.py
@@ -21,7 +21,7 @@ from sys import exit
from vyos import ConfigError
from vyos.config import Config
-from vyos.util import call
+from vyos.utils.process import call
from vyos.template import render
from vyos import airbag
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index 1be2c283f..ad4121a49 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -36,9 +36,8 @@ from vyos.qos import RateLimiter
from vyos.qos import RoundRobin
from vyos.qos import TrafficShaper
from vyos.qos import TrafficShaperHFSC
-from vyos.util import call
-from vyos.util import dict_search_recursive
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search_recursive
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -85,75 +84,43 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
- if 'interface' in qos:
- for ifname, if_conf in qos['interface'].items():
- if_node = Section.get_config_path(ifname)
+ for ifname in interfaces():
+ if_node = Section.get_config_path(ifname)
- if not if_node:
- continue
+ if not if_node:
+ continue
- path = f'interfaces {if_node}'
- if conf.exists(f'{path} mirror') or conf.exists(f'{path} redirect'):
- type_node = path.split(" ")[1] # return only interface type node
- set_dependents(type_node, conf, ifname)
-
- if 'policy' in qos:
- for policy in qos['policy']:
- # when calling defaults() we need to use the real CLI node, thus we
- # need a hyphen
- policy_hyphen = policy.replace('_', '-')
-
- if policy in ['random_detect']:
- for rd_name, rd_config in qos['policy'][policy].items():
- # There are eight precedence levels - ensure all are present
- # to be filled later down with the appropriate default values
- default_precedence = {'precedence' : { '0' : {}, '1' : {}, '2' : {}, '3' : {},
- '4' : {}, '5' : {}, '6' : {}, '7' : {} }}
- qos['policy']['random_detect'][rd_name] = dict_merge(
- default_precedence, qos['policy']['random_detect'][rd_name])
-
- for p_name, p_config in qos['policy'][policy].items():
- default_values = defaults(base + ['policy', policy_hyphen])
-
- if policy in ['priority_queue']:
- if 'default' not in p_config:
- raise ConfigError(f'QoS policy {p_name} misses "default" class!')
-
- # 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 'class' in default_values:
- del default_values['class']
- if 'precedence' in default_values:
- del default_values['precedence']
-
- qos['policy'][policy][p_name] = dict_merge(
- default_values, qos['policy'][policy][p_name])
-
- # class is another tag node which requires individual handling
- if 'class' in p_config:
- default_values = defaults(base + ['policy', policy_hyphen, 'class'])
- for p_class in p_config['class']:
- qos['policy'][policy][p_name]['class'][p_class] = dict_merge(
- default_values, qos['policy'][policy][p_name]['class'][p_class])
-
- if 'precedence' in p_config:
- default_values = defaults(base + ['policy', policy_hyphen, 'precedence'])
- # precedence values are a bit more complex as they are calculated
- # under specific circumstances - thus we need to iterate two times.
- # first blend in the defaults from XML / CLI
- for precedence in p_config['precedence']:
- qos['policy'][policy][p_name]['precedence'][precedence] = dict_merge(
- default_values, qos['policy'][policy][p_name]['precedence'][precedence])
- # second calculate defaults based on actual dictionary
- for precedence in p_config['precedence']:
- max_thr = int(qos['policy'][policy][p_name]['precedence'][precedence]['maximum_threshold'])
- if 'minimum_threshold' not in qos['policy'][policy][p_name]['precedence'][precedence]:
- qos['policy'][policy][p_name]['precedence'][precedence]['minimum_threshold'] = str(
- int((9 + int(precedence)) * max_thr) // 18);
-
- if 'queue_limit' not in qos['policy'][policy][p_name]['precedence'][precedence]:
- qos['policy'][policy][p_name]['precedence'][precedence]['queue_limit'] = \
- str(int(4 * max_thr))
+ path = f'interfaces {if_node}'
+ if conf.exists(f'{path} mirror') or conf.exists(f'{path} redirect'):
+ type_node = path.split(" ")[1] # return only interface type node
+ set_dependents(type_node, conf, ifname.split(".")[0])
+
+ for policy in qos.get('policy', []):
+ if policy in ['random_detect']:
+ for rd_name in list(qos['policy'][policy]):
+ # There are eight precedence levels - ensure all are present
+ # to be filled later down with the appropriate default values
+ default_precedence = {'precedence' : { '0' : {}, '1' : {}, '2' : {}, '3' : {},
+ '4' : {}, '5' : {}, '6' : {}, '7' : {} }}
+ qos['policy']['random_detect'][rd_name] = dict_merge(
+ default_precedence, qos['policy']['random_detect'][rd_name])
+
+ qos = conf.merge_defaults(qos, recursive=True)
+
+ for policy in qos.get('policy', []):
+ for p_name, p_config in qos['policy'][policy].items():
+ if 'precedence' in p_config:
+ # precedence settings are a bit more complex as they are
+ # calculated under specific circumstances:
+ for precedence in p_config['precedence']:
+ max_thr = int(qos['policy'][policy][p_name]['precedence'][precedence]['maximum_threshold'])
+ if 'minimum_threshold' not in qos['policy'][policy][p_name]['precedence'][precedence]:
+ qos['policy'][policy][p_name]['precedence'][precedence]['minimum_threshold'] = str(
+ int((9 + int(precedence)) * max_thr) // 18);
+
+ if 'queue_limit' not in qos['policy'][policy][p_name]['precedence'][precedence]:
+ qos['policy'][policy][p_name]['precedence'][precedence]['queue_limit'] = \
+ str(int(4 * max_thr))
return qos
@@ -202,7 +169,9 @@ def verify(qos):
queue_lim = int(precedence_config['queue_limit'])
if queue_lim < max_tr:
raise ConfigError(f'Policy "{policy}" uses queue-limit "{queue_lim}" < max-threshold "{max_tr}"!')
-
+ if policy_type in ['priority_queue']:
+ if 'default' not in policy_config:
+ raise ConfigError(f'Policy {policy} misses "default" class!')
if 'default' in policy_config:
if 'bandwidth' not in policy_config['default'] and policy_type not in ['priority_queue', 'round_robin']:
raise ConfigError('Bandwidth not defined for default traffic!')
@@ -239,6 +208,8 @@ def apply(qos):
call(f'tc qdisc del dev {interface} parent ffff:')
call(f'tc qdisc del dev {interface} root')
+ call_dependents()
+
if not qos or 'interface' not in qos:
return None
@@ -259,8 +230,6 @@ def apply(qos):
tmp = shaper_type(interface)
tmp.update(shaper_config, direction)
- call_dependents()
-
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py
index 00b889a11..a8fce8e01 100755
--- a/src/conf_mode/salt-minion.py
+++ b/src/conf_mode/salt-minion.py
@@ -22,12 +22,10 @@ from urllib3 import PoolManager
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configverify import verify_interface_exists
from vyos.template import render
-from vyos.util import call
-from vyos.util import chown
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.permission import chown
from vyos import ConfigError
from vyos import airbag
@@ -55,8 +53,7 @@ def get_config(config=None):
salt['id'] = gethostname()
# 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)
- salt = dict_merge(default_values, salt)
+ salt = conf.merge_defaults(salt, recursive=True)
if not conf.exists(base):
return None
diff --git a/src/conf_mode/service_config_sync.py b/src/conf_mode/service_config_sync.py
new file mode 100755
index 000000000..4b8a7f6ee
--- /dev/null
+++ b/src/conf_mode/service_config_sync.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import json
+from pathlib import Path
+
+from vyos.config import Config
+from vyos import ConfigError
+from vyos import airbag
+
+airbag.enable()
+
+
+service_conf = Path(f'/run/config_sync_conf.conf')
+post_commit_dir = '/run/scripts/commit/post-hooks.d'
+post_commit_file_src = '/usr/libexec/vyos/vyos_config_sync.py'
+post_commit_file = f'{post_commit_dir}/vyos_config_sync'
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['service', 'config-sync']
+ if not conf.exists(base):
+ return None
+ config = conf.get_config_dict(base, get_first_key=True,
+ with_recursive_defaults=True)
+
+ return config
+
+
+def verify(config):
+ # bail out early - looks like removal from running config
+ if not config:
+ return None
+
+ if 'mode' not in config:
+ raise ConfigError(f'config-sync mode is mandatory!')
+
+ for option in ['secondary', 'section']:
+ if option not in config:
+ raise ConfigError(f"config-sync '{option}' is not configured!")
+
+ if 'address' not in config['secondary']:
+ raise ConfigError(f'secondary address is mandatory!')
+ if 'key' not in config['secondary']:
+ raise ConfigError(f'secondary key is mandatory!')
+
+
+def generate(config):
+ if not config:
+
+ if os.path.exists(post_commit_file):
+ os.unlink(post_commit_file)
+
+ if service_conf.exists():
+ service_conf.unlink()
+
+ return None
+
+ # Write configuration file
+ conf_json = json.dumps(config, indent=4)
+ service_conf.write_text(conf_json)
+
+ # Create post commit dir
+ if not os.path.isdir(post_commit_dir):
+ os.makedirs(post_commit_dir)
+
+ # Symlink from helpers to post-commit
+ if not os.path.exists(post_commit_file):
+ os.symlink(post_commit_file_src, post_commit_file)
+
+ return None
+
+
+def apply(config):
+ 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_console-server.py b/src/conf_mode/service_console-server.py
index 60eff6543..b112add3f 100755
--- a/src/conf_mode/service_console-server.py
+++ b/src/conf_mode/service_console-server.py
@@ -20,10 +20,8 @@ from sys import exit
from psutil import process_iter
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
config_file = '/run/conserver/conserver.cf'
@@ -49,11 +47,7 @@ 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 + ['device'])
- if 'device' in proxy:
- for device in proxy['device']:
- tmp = dict_merge(default_values, proxy['device'][device])
- proxy['device'][device] = tmp
+ proxy = conf.merge_defaults(proxy, recursive=True)
return proxy
diff --git a/src/conf_mode/service_event_handler.py b/src/conf_mode/service_event_handler.py
index 5440d1056..5028ef52f 100755
--- a/src/conf_mode/service_event_handler.py
+++ b/src/conf_mode/service_event_handler.py
@@ -18,7 +18,8 @@ import json
from pathlib import Path
from vyos.config import Config
-from vyos.util import call, dict_search
+from vyos.utils.dict import dict_search
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py
index c58f8db9a..276a71fcb 100755
--- a/src/conf_mode/service_ids_fastnetmon.py
+++ b/src/conf_mode/service_ids_fastnetmon.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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,10 +19,8 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -30,6 +28,7 @@ airbag.enable()
config_file = r'/run/fastnetmon/fastnetmon.conf'
networks_list = r'/run/fastnetmon/networks_list'
excluded_networks_list = r'/run/fastnetmon/excluded_networks_list'
+attack_dir = '/var/log/fastnetmon_attacks'
def get_config(config=None):
if config:
@@ -40,11 +39,9 @@ def get_config(config=None):
if not conf.exists(base):
return None
- fastnetmon = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- fastnetmon = dict_merge(default_values, fastnetmon)
+ fastnetmon = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return fastnetmon
@@ -55,8 +52,11 @@ def verify(fastnetmon):
if 'mode' not in fastnetmon:
raise ConfigError('Specify operating mode!')
- if 'listen_interface' not in fastnetmon:
- raise ConfigError('Specify interface(s) for traffic capture')
+ if fastnetmon.get('mode') == 'mirror' and 'listen_interface' not in fastnetmon:
+ raise ConfigError("Incorrect settings for 'mode mirror': must specify interface(s) for traffic mirroring")
+
+ if fastnetmon.get('mode') == 'sflow' and 'listen_address' not in fastnetmon.get('sflow', {}):
+ raise ConfigError("Incorrect settings for 'mode sflow': must specify sFlow 'listen-address'")
if 'alert_script' in fastnetmon:
if os.path.isfile(fastnetmon['alert_script']):
@@ -74,6 +74,10 @@ def generate(fastnetmon):
return None
+ # Create dir for log attack details
+ if not os.path.exists(attack_dir):
+ os.mkdir(attack_dir)
+
render(config_file, 'ids/fastnetmon.j2', fastnetmon)
render(networks_list, 'ids/fastnetmon_networks_list.j2', fastnetmon)
render(excluded_networks_list, 'ids/fastnetmon_excluded_networks_list.j2', fastnetmon)
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 95c72df47..b70e32373 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -24,8 +24,8 @@ from vyos.configdict import get_accel_dict
from vyos.configverify import verify_accel_ppp_base_service
from vyos.configverify import verify_interface_exists
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/service_mdns-repeater.py b/src/conf_mode/service_mdns-repeater.py
index 2383a53fb..a2c90b537 100755
--- a/src/conf_mode/service_mdns-repeater.py
+++ b/src/conf_mode/service_mdns-repeater.py
@@ -23,7 +23,7 @@ from netifaces import ifaddresses, interfaces, AF_INET
from vyos.config import Config
from vyos.ifconfig.vrrp import VRRP
from vyos.template import render
-from vyos.util import call
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py
index 47510ce80..40eb13e23 100755
--- a/src/conf_mode/service_monitoring_telegraf.py
+++ b/src/conf_mode/service_monitoring_telegraf.py
@@ -22,15 +22,13 @@ from sys import exit
from shutil import rmtree
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.ifconfig import Section
from vyos.template import render
-from vyos.util import call
-from vyos.util import chown
-from vyos.util import cmd
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.permission import chown
+from vyos.utils.process import cmd
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -83,8 +81,7 @@ 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)
- monitoring = dict_merge(default_values, monitoring)
+ monitoring = conf.merge_defaults(monitoring, recursive=True)
monitoring['custom_scripts_dir'] = custom_scripts_dir
monitoring['hostname'] = get_hostname()
diff --git a/src/conf_mode/service_monitoring_zabbix-agent.py b/src/conf_mode/service_monitoring_zabbix-agent.py
new file mode 100755
index 000000000..98d8a32ca
--- /dev/null
+++ b/src/conf_mode/service_monitoring_zabbix-agent.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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 vyos.config import Config
+from vyos.template import render
+from vyos.utils.process import call
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+
+service_name = 'zabbix-agent2'
+service_conf = f'/run/zabbix/{service_name}.conf'
+systemd_override = r'/run/systemd/system/zabbix-agent2.service.d/10-override.conf'
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['service', 'monitoring', 'zabbix-agent']
+
+ if not conf.exists(base):
+ return None
+
+ config = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ with_recursive_defaults=True)
+
+ # Cut the / from the end, /tmp/ => /tmp
+ if 'directory' in config and config['directory'].endswith('/'):
+ config['directory'] = config['directory'][:-1]
+
+ return config
+
+
+def verify(config):
+ # bail out early - looks like removal from running config
+ if config is None:
+ return
+
+ if 'server' not in config:
+ raise ConfigError('Server is required!')
+
+
+def generate(config):
+ # bail out early - looks like removal from running config
+ if config is None:
+ # Remove old config and return
+ config_files = [service_conf, systemd_override]
+ for file in config_files:
+ if os.path.isfile(file):
+ os.unlink(file)
+
+ return None
+
+ # Write configuration file
+ render(service_conf, 'zabbix-agent/zabbix-agent.conf.j2', config)
+ render(systemd_override, 'zabbix-agent/10-override.conf.j2', config)
+
+ return None
+
+
+def apply(config):
+ call('systemctl daemon-reload')
+ if config:
+ call(f'systemctl restart {service_name}.service')
+ else:
+ call(f'systemctl stop {service_name}.service')
+
+
+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_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index adeefaa37..aace267a7 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -24,8 +24,8 @@ from vyos.configdict import is_node_changed
from vyos.configverify import verify_accel_ppp_base_service
from vyos.configverify import verify_interface_exists
from vyos.template import render
-from vyos.util import call
-from vyos.util import dict_search
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py
index 1dd973d67..dbb47de4e 100755
--- a/src/conf_mode/service_router-advert.py
+++ b/src/conf_mode/service_router-advert.py
@@ -19,10 +19,8 @@ import os
from sys import exit
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -35,40 +33,9 @@ def get_config(config=None):
else:
conf = Config()
base = ['service', 'router-advert']
- rtradv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_interface_values = defaults(base + ['interface'])
- # we deal with prefix, route defaults later on
- if 'prefix' in default_interface_values:
- del default_interface_values['prefix']
- if 'route' in default_interface_values:
- del default_interface_values['route']
-
- default_prefix_values = defaults(base + ['interface', 'prefix'])
- default_route_values = defaults(base + ['interface', 'route'])
-
- if 'interface' in rtradv:
- for interface in rtradv['interface']:
- rtradv['interface'][interface] = dict_merge(
- default_interface_values, rtradv['interface'][interface])
-
- if 'prefix' in rtradv['interface'][interface]:
- for prefix in rtradv['interface'][interface]['prefix']:
- rtradv['interface'][interface]['prefix'][prefix] = dict_merge(
- default_prefix_values, rtradv['interface'][interface]['prefix'][prefix])
-
- if 'route' in rtradv['interface'][interface]:
- for route in rtradv['interface'][interface]['route']:
- rtradv['interface'][interface]['route'][route] = dict_merge(
- default_route_values, rtradv['interface'][interface]['route'][route])
-
- if 'name_server' in rtradv['interface'][interface]:
- # always use a list when dealing with nameservers - eases the template generation
- if isinstance(rtradv['interface'][interface]['name_server'], str):
- rtradv['interface'][interface]['name_server'] = [
- rtradv['interface'][interface]['name_server']]
+ rtradv = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return rtradv
diff --git a/src/conf_mode/service_sla.py b/src/conf_mode/service_sla.py
index b1e22f37b..ba5e645f0 100755
--- a/src/conf_mode/service_sla.py
+++ b/src/conf_mode/service_sla.py
@@ -19,10 +19,8 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.xml import defaults
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -44,11 +42,9 @@ def get_config(config=None):
if not conf.exists(base):
return None
- sla = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- sla = dict_merge(default_values, sla)
+ sla = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
# Ignore default XML values if config doesn't exists
# Delete key from dict
diff --git a/src/conf_mode/service_upnp.py b/src/conf_mode/service_upnp.py
index c798fd515..cf26bf9ce 100755
--- a/src/conf_mode/service_upnp.py
+++ b/src/conf_mode/service_upnp.py
@@ -23,12 +23,10 @@ from ipaddress import IPv4Network
from ipaddress import IPv6Network
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.util import call
+from vyos.utils.process 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()
@@ -47,10 +45,7 @@ def get_config(config=None):
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])
+ upnpd = conf.merge_defaults(upnpd, recursive=True)
uuidgen = uuid.uuid1()
upnpd.update({'uuid': uuidgen})
diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py
index 658e496a6..12ae4135e 100755
--- a/src/conf_mode/service_webproxy.py
+++ b/src/conf_mode/service_webproxy.py
@@ -20,14 +20,13 @@ from shutil import rmtree
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.config import config_dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.util import chmod_755
-from vyos.util import dict_search
-from vyos.util import write_file
-from vyos.validate import is_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.permission import chmod_755
+from vyos.utils.dict import dict_search
+from vyos.utils.file import write_file
+from vyos.utils.network import is_addr_assigned
from vyos.base import Warning
from vyos import ConfigError
from vyos import airbag
@@ -125,7 +124,8 @@ def get_config(config=None):
get_first_key=True)
# 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)
+ default_values = conf.get_config_defaults(**proxy.kwargs,
+ recursive=True)
# if no authentication method is supplied, no need to add defaults
if not dict_search('authentication.method', proxy):
@@ -138,16 +138,7 @@ def get_config(config=None):
proxy['squidguard_conf'] = squidguard_config_file
proxy['squidguard_db_dir'] = squidguard_db_dir
- # XXX: T2665: blend in proper cache-peer default values later
- default_values.pop('cache_peer')
- proxy = dict_merge(default_values, proxy)
-
- # XXX: T2665: blend in proper cache-peer default values
- if 'cache_peer' in proxy:
- default_values = defaults(base + ['cache-peer'])
- for peer in proxy['cache_peer']:
- proxy['cache_peer'][peer] = dict_merge(default_values,
- proxy['cache_peer'][peer])
+ proxy = config_dict_merge(default_values, proxy)
return proxy
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 9b7c04eb0..7882f8510 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -26,12 +26,11 @@ from vyos.snmpv3_hashgen import plaintext_to_md5
from vyos.snmpv3_hashgen import plaintext_to_sha1
from vyos.snmpv3_hashgen import random
from vyos.template import render
-from vyos.util import call
-from vyos.util import chmod_755
-from vyos.util import dict_search
-from vyos.validate import is_addr_assigned
+from vyos.utils.process import call
+from vyos.utils.permission import chmod_755
+from vyos.utils.dict import dict_search
+from vyos.utils.network import is_addr_assigned
from vyos.version import get_version_data
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -70,26 +69,9 @@ 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)
-
- # We can not merge defaults for tagNodes - those need to be blended in
- # per tagNode instance
- if 'listen_address' in default_values:
- del default_values['listen_address']
- if 'community' in default_values:
- del default_values['community']
- if 'trap_target' in default_values:
- del default_values['trap_target']
- if 'v3' in default_values:
- del default_values['v3']
- snmp = dict_merge(default_values, snmp)
+ snmp = conf.merge_defaults(snmp, recursive=True)
if 'listen_address' in snmp:
- default_values = defaults(base + ['listen-address'])
- for address in snmp['listen_address']:
- snmp['listen_address'][address] = dict_merge(
- default_values, snmp['listen_address'][address])
-
# Always listen on localhost if an explicit address has been configured
# This is a safety measure to not end up with invalid listen addresses
# that are not configured on this system. See https://vyos.dev/T850
@@ -101,41 +83,6 @@ def get_config(config=None):
tmp = {'::1': {'port': '161'}}
snmp['listen_address'] = dict_merge(tmp, snmp['listen_address'])
- if 'community' in snmp:
- default_values = defaults(base + ['community'])
- if 'network' in default_values:
- # convert multiple default networks to list
- default_values['network'] = default_values['network'].split()
- for community in snmp['community']:
- snmp['community'][community] = dict_merge(
- default_values, snmp['community'][community])
-
- if 'trap_target' in snmp:
- default_values = defaults(base + ['trap-target'])
- for trap in snmp['trap_target']:
- snmp['trap_target'][trap] = dict_merge(
- default_values, snmp['trap_target'][trap])
-
- if 'v3' in snmp:
- default_values = defaults(base + ['v3'])
- # tagNodes need to be merged in individually later on
- for tmp in ['user', 'group', 'trap_target']:
- del default_values[tmp]
- snmp['v3'] = dict_merge(default_values, snmp['v3'])
-
- for user_group in ['user', 'group']:
- if user_group in snmp['v3']:
- default_values = defaults(base + ['v3', user_group])
- for tmp in snmp['v3'][user_group]:
- snmp['v3'][user_group][tmp] = dict_merge(
- default_values, snmp['v3'][user_group][tmp])
-
- if 'trap_target' in snmp['v3']:
- default_values = defaults(base + ['v3', 'trap-target'])
- for trap in snmp['v3']['trap_target']:
- snmp['v3']['trap_target'][trap] = dict_merge(
- default_values, snmp['v3']['trap_target'][trap])
-
return snmp
def verify(snmp):
@@ -161,8 +108,12 @@ def verify(snmp):
for address in snmp['listen_address']:
# We only wan't to configure addresses that exist on the system.
# Hint the user if they don't exist
- if not is_addr_assigned(address):
- Warning(f'SNMP listen address "{address}" not configured!')
+ if 'vrf' in snmp:
+ vrf_name = snmp['vrf']
+ if not is_addr_assigned(address, vrf_name) and address not in ['::1','127.0.0.1']:
+ raise ConfigError(f'SNMP listen address "{address}" not configured in vrf "{vrf_name}"!')
+ elif not is_addr_assigned(address):
+ raise ConfigError(f'SNMP listen address "{address}" not configured in default vrf!')
if 'trap_target' in snmp:
for trap, trap_config in snmp['trap_target'].items():
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index 8de0617af..ee5e1eca2 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -21,12 +21,10 @@ from syslog import syslog
from syslog import LOG_INFO
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
-from vyos.util import call
+from vyos.utils.process import call
from vyos.template import render
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -57,8 +55,8 @@ 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)
- ssh = dict_merge(default_values, ssh)
+ ssh = conf.merge_defaults(ssh, recursive=True)
+
# pass config file path - used in override template
ssh['config_file'] = config_file
diff --git a/src/conf_mode/system-ip.py b/src/conf_mode/system-ip.py
index 95865c690..5e4e5ec28 100755
--- a/src/conf_mode/system-ip.py
+++ b/src/conf_mode/system-ip.py
@@ -20,11 +20,10 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
-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.utils.process import call
+from vyos.utils.dict import dict_search
+from vyos.utils.file import write_file
+from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -37,11 +36,9 @@ def get_config(config=None):
conf = Config()
base = ['system', 'ip']
- opt = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
+ opt = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
# When working with FRR we need to know the corresponding address-family
opt['afi'] = 'ip'
@@ -64,8 +61,7 @@ def verify(opt):
return
def generate(opt):
- if 'protocol' in opt:
- opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
+ opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
return
def apply(opt):
@@ -98,17 +94,37 @@ def apply(opt):
value = '1' if (tmp != None) else '0'
sysctl_write('net.ipv4.fib_multipath_hash_policy', value)
- if 'protocol' in opt:
- zebra_daemon = 'zebra'
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(zebra_daemon)
- frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- if 'frr_zebra_config' in opt:
- frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
- frr_cfg.commit_configuration(zebra_daemon)
+ # configure TCP options (defaults as of Linux 6.4)
+ tmp = dict_search('tcp.mss.probing', opt)
+ if tmp is None:
+ value = 0
+ elif tmp == 'on-icmp-black-hole':
+ value = 1
+ elif tmp == 'force':
+ value = 2
+ else:
+ # Shouldn't happen
+ raise ValueError("TCP MSS probing is neither 'on-icmp-black-hole' nor 'force'!")
+ sysctl_write('net.ipv4.tcp_mtu_probing', value)
+
+ tmp = dict_search('tcp.mss.base', opt)
+ value = '1024' if (tmp is None) else tmp
+ sysctl_write('net.ipv4.tcp_base_mss', value)
+
+ tmp = dict_search('tcp.mss.floor', opt)
+ value = '48' if (tmp is None) else tmp
+ sysctl_write('net.ipv4.tcp_mtu_probe_floor', value)
+
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
+ if 'frr_zebra_config' in opt:
+ frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py
index b6d3a79c3..e40ed38e2 100755
--- a/src/conf_mode/system-ipv6.py
+++ b/src/conf_mode/system-ipv6.py
@@ -21,10 +21,9 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
-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.utils.dict import dict_search
+from vyos.utils.system import sysctl_write
+from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -37,12 +36,9 @@ def get_config(config=None):
conf = Config()
base = ['system', 'ipv6']
- opt = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
- # 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)
+ opt = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
# When working with FRR we need to know the corresponding address-family
opt['afi'] = 'ipv6'
@@ -65,8 +61,7 @@ def verify(opt):
return
def generate(opt):
- if 'protocol' in opt:
- opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
+ opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
return
def apply(opt):
@@ -98,17 +93,16 @@ def apply(opt):
if name == 'accept_dad':
write_file(os.path.join(root, name), value)
- if 'protocol' in opt:
- zebra_daemon = 'zebra'
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(zebra_daemon)
- frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- if 'frr_zebra_config' in opt:
- frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
- frr_cfg.commit_configuration(zebra_daemon)
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
+ if 'frr_zebra_config' in opt:
+ frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system-login-banner.py b/src/conf_mode/system-login-banner.py
index a521c9834..65fa04417 100755
--- a/src/conf_mode/system-login-banner.py
+++ b/src/conf_mode/system-login-banner.py
@@ -18,7 +18,7 @@ from sys import exit
from copy import deepcopy
from vyos.config import Config
-from vyos.util import write_file
+from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index fbb013cf3..02c97afaa 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -24,17 +24,16 @@ from sys import exit
from time import sleep
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configverify import verify_vrf
from vyos.defaults import directories
from vyos.template import render
from vyos.template import is_ipv4
-from vyos.util import cmd
-from vyos.util import call, rc_cmd
-from vyos.util import run
-from vyos.util import DEVNULL
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.process import call
+from vyos.utils.process import rc_cmd
+from vyos.utils.process import run
+from vyos.utils.process import DEVNULL
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -42,20 +41,38 @@ airbag.enable()
autologout_file = "/etc/profile.d/autologout.sh"
limits_file = "/etc/security/limits.d/10-vyos.conf"
radius_config_file = "/etc/pam_radius_auth.conf"
-
+tacacs_pam_config_file = "/etc/tacplus_servers"
+tacacs_nss_config_file = "/etc/tacplus_nss.conf"
+nss_config_file = "/etc/nsswitch.conf"
+
+# Minimum UID used when adding system users
+MIN_USER_UID: int = 1000
+# Maximim UID used when adding system users
+MAX_USER_UID: int = 59999
# LOGIN_TIMEOUT from /etc/loign.defs minus 10 sec
MAX_RADIUS_TIMEOUT: int = 50
# MAX_RADIUS_TIMEOUT divided by 2 sec (minimum recomended timeout)
-MAX_RADIUS_COUNT: int = 25
+MAX_RADIUS_COUNT: int = 8
+# Maximum number of supported TACACS servers
+MAX_TACACS_COUNT: int = 8
+
+# List of local user accounts that must be preserved
+SYSTEM_USER_SKIP_LIST: list = ['radius_user', 'radius_priv_user', 'tacacs0', 'tacacs1',
+ 'tacacs2', 'tacacs3', 'tacacs4', 'tacacs5', 'tacacs6',
+ 'tacacs7', 'tacacs8', 'tacacs9', 'tacacs10',' tacacs11',
+ 'tacacs12', 'tacacs13', 'tacacs14', 'tacacs15']
def get_local_users():
"""Return list of dynamically allocated users (see Debian Policy Manual)"""
local_users = []
for s_user in getpwall():
- uid = getpwnam(s_user.pw_name).pw_uid
- if uid in range(1000, 29999):
- if s_user.pw_name not in ['radius_user', 'radius_priv_user']:
- local_users.append(s_user.pw_name)
+ if getpwnam(s_user.pw_name).pw_uid < MIN_USER_UID:
+ continue
+ if getpwnam(s_user.pw_name).pw_uid > MAX_USER_UID:
+ continue
+ if s_user.pw_name in SYSTEM_USER_SKIP_LIST:
+ continue
+ local_users.append(s_user.pw_name)
return local_users
@@ -74,7 +91,9 @@ def get_config(config=None):
conf = Config()
base = ['system', 'login']
login = conf.get_config_dict(base, key_mangling=('-', '_'),
- no_tag_node_value_mangle=True, get_first_key=True)
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
# users no longer existing in the running configuration need to be deleted
local_users = get_local_users()
@@ -82,18 +101,9 @@ def get_config(config=None):
if 'user' in login:
cli_users = list(login['user'])
- # 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.
- default_values = defaults(base + ['user'])
- for user in login['user']:
- login['user'][user] = dict_merge(default_values, login['user'][user])
-
- # 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.
- default_values = defaults(base + ['radius', 'server'])
- for server in dict_search('radius.server', login) or []:
- login['radius']['server'][server] = dict_merge(default_values,
- login['radius']['server'][server])
+ # prune TACACS global defaults if not set by user
+ if login.from_defaults(['tacacs']):
+ del login['tacacs']
# create a list of all users, cli and users
all_users = list(set(local_users + cli_users))
@@ -107,9 +117,13 @@ def get_config(config=None):
def verify(login):
if 'rm_users' in login:
- cur_user = os.environ['SUDO_USER']
- if cur_user in login['rm_users']:
- raise ConfigError(f'Attempting to delete current user: {cur_user}')
+ # This check is required as the script is also executed from vyos-router
+ # init script and there is no SUDO_USER environment variable available
+ # during system boot.
+ if 'SUDO_USER' in os.environ:
+ cur_user = os.environ['SUDO_USER']
+ if cur_user in login['rm_users']:
+ raise ConfigError(f'Attempting to delete current user: {cur_user}')
if 'user' in login:
system_users = getpwall()
@@ -117,7 +131,7 @@ def verify(login):
# Linux system users range up until UID 1000, we can not create a
# VyOS CLI user which already exists as system user
for s_user in system_users:
- if s_user.pw_name == user and s_user.pw_uid < 1000:
+ if s_user.pw_name == user and s_user.pw_uid < MIN_USER_UID:
raise ConfigError(f'User "{user}" can not be created, conflict with local system account!')
for pubkey, pubkey_options in (dict_search('authentication.public_keys', user_config) or {}).items():
@@ -126,6 +140,9 @@ def verify(login):
if 'key' not in pubkey_options:
raise ConfigError(f'Missing key for public-key "{pubkey}"!')
+ if {'radius', 'tacacs'} <= set(login):
+ raise ConfigError('Using both RADIUS and TACACS at the same time is not supported!')
+
# At lease one RADIUS server must not be disabled
if 'radius' in login:
if 'server' not in login['radius']:
@@ -145,7 +162,7 @@ def verify(login):
raise ConfigError('All RADIUS servers are disabled')
if radius_servers_count > MAX_RADIUS_COUNT:
- raise ConfigError('Number of RADIUS servers more than 25 ')
+ raise ConfigError(f'Number of RADIUS servers exceeded maximum of {MAX_RADIUS_COUNT}!')
if sum_timeout > MAX_RADIUS_TIMEOUT:
raise ConfigError('Sum of RADIUS servers timeouts '
@@ -165,6 +182,24 @@ def verify(login):
if ipv6_count > 1:
raise ConfigError('Only one IPv6 source-address can be set!')
+ if 'tacacs' in login:
+ tacacs_servers_count: int = 0
+ fail = True
+ for server, server_config in dict_search('tacacs.server', login).items():
+ if 'key' not in server_config:
+ raise ConfigError(f'TACACS server "{server}" requires key!')
+ if 'disable' not in server_config:
+ tacacs_servers_count += 1
+ fail = False
+
+ if fail:
+ raise ConfigError('All RADIUS servers are disabled')
+
+ if tacacs_servers_count > MAX_TACACS_COUNT:
+ raise ConfigError(f'Number of TACACS servers exceeded maximum of {MAX_TACACS_COUNT}!')
+
+ verify_vrf(login['tacacs'])
+
if 'max_login_session' in login and 'timeout' not in login:
raise ConfigError('"login timeout" must be configured!')
@@ -186,8 +221,8 @@ def generate(login):
env['vyos_libexec_dir'] = directories['base']
# Set default commands for re-adding user with encrypted password
- del_user_plain = f"system login user '{user}' authentication plaintext-password"
- add_user_encrypt = f"system login user '{user}' authentication encrypted-password '{encrypted_password}'"
+ del_user_plain = f"system login user {user} authentication plaintext-password"
+ add_user_encrypt = f"system login user {user} authentication encrypted-password '{encrypted_password}'"
lvl = env['VYATTA_EDIT_LEVEL']
# We're in config edit level, for example "edit system login"
@@ -206,10 +241,10 @@ def generate(login):
add_user_encrypt = add_user_encrypt[len(lvl):]
add_user_encrypt = " ".join(add_user_encrypt)
- call(f"/opt/vyatta/sbin/my_delete {del_user_plain}", env=env)
+ ret, out = rc_cmd(f"/opt/vyatta/sbin/my_delete {del_user_plain}", env=env)
+ if ret: raise ConfigError(out)
ret, out = rc_cmd(f"/opt/vyatta/sbin/my_set {add_user_encrypt}", env=env)
- if ret:
- raise ConfigError(out)
+ if ret: raise ConfigError(out)
else:
try:
if get_shadow_password(user) == dict_search('authentication.encrypted_password', user_config):
@@ -223,6 +258,7 @@ def generate(login):
except:
pass
+ ### RADIUS based user authentication
if 'radius' in login:
render(radius_config_file, 'login/pam_radius_auth.conf.j2', login,
permission=0o600, user='root', group='root')
@@ -230,6 +266,24 @@ def generate(login):
if os.path.isfile(radius_config_file):
os.unlink(radius_config_file)
+ ### TACACS+ based user authentication
+ if 'tacacs' in login:
+ render(tacacs_pam_config_file, 'login/tacplus_servers.j2', login,
+ permission=0o644, user='root', group='root')
+ render(tacacs_nss_config_file, 'login/tacplus_nss.conf.j2', login,
+ permission=0o644, user='root', group='root')
+ else:
+ if os.path.isfile(tacacs_pam_config_file):
+ os.unlink(tacacs_pam_config_file)
+ if os.path.isfile(tacacs_nss_config_file):
+ os.unlink(tacacs_nss_config_file)
+
+
+
+ # NSS must always be present on the system
+ render(nss_config_file, 'login/nsswitch.conf.j2', login,
+ permission=0o644, user='root', group='root')
+
# /etc/security/limits.d/10-vyos.conf
if 'max_login_session' in login:
render(limits_file, 'login/limits.j2', login,
@@ -253,7 +307,7 @@ def apply(login):
for user, user_config in login['user'].items():
# make new user using vyatta shell and make home directory (-m),
# default group of 100 (users)
- command = 'useradd --create-home --no-user-group'
+ command = 'useradd --create-home --no-user-group '
# check if user already exists:
if user in get_local_users():
# update existing account
@@ -317,44 +371,23 @@ def apply(login):
# 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):
+ while run(f'userdel {user}', stderr=DEVNULL):
sleep(0.250)
except Exception as e:
raise ConfigError(f'Deleting user "{user}" raised exception: {e}')
- #
- # RADIUS configuration
- #
- env = os.environ.copy()
- env['DEBIAN_FRONTEND'] = 'noninteractive'
- try:
- if 'radius' in login:
- # Enable RADIUS in PAM
- cmd('pam-auth-update --package --enable radius', env=env)
- # Make NSS system aware of RADIUS
- # This fancy snipped was copied from old Vyatta code
- command = "sed -i -e \'/\smapname/b\' \
- -e \'/^passwd:/s/\s\s*/&mapuid /\' \
- -e \'/^passwd:.*#/s/#.*/mapname &/\' \
- -e \'/^passwd:[^#]*$/s/$/ mapname &/\' \
- -e \'/^group:.*#/s/#.*/ mapname &/\' \
- -e \'/^group:[^#]*$/s/: */&mapname /\' \
- /etc/nsswitch.conf"
- else:
- # Disable RADIUS in PAM
- cmd('pam-auth-update --package --remove radius', env=env)
- # Drop RADIUS from NSS NSS system
- # This fancy snipped was copied from old Vyatta code
- command = "sed -i -e \'/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//\' \
- -e \'/^passwd:.*[ \t]mapname/s/[ \t]mapname//\' \
- -e \'/^group:.*[ \t]mapname/s/[ \t]mapname//\' \
- -e \'s/[ \t]*$//\' \
- /etc/nsswitch.conf"
-
- cmd(command)
- except Exception as e:
- raise ConfigError(f'RADIUS configuration failed: {e}')
+ # Enable RADIUS in PAM configuration
+ pam_cmd = '--remove'
+ if 'radius' in login:
+ pam_cmd = '--enable'
+ cmd(f'pam-auth-update --package {pam_cmd} radius')
+
+ # Enable/Disable TACACS in PAM configuration
+ pam_cmd = '--remove'
+ if 'tacacs' in login:
+ pam_cmd = '--enable'
+ cmd(f'pam-auth-update --package {pam_cmd} tacplus')
return None
diff --git a/src/conf_mode/system-logs.py b/src/conf_mode/system-logs.py
index c71938a79..8ad4875d4 100755
--- a/src/conf_mode/system-logs.py
+++ b/src/conf_mode/system-logs.py
@@ -19,11 +19,9 @@ from sys import exit
from vyos import ConfigError
from vyos import airbag
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.logger import syslog
from vyos.template import render
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.dict import dict_search
airbag.enable()
# path to logrotate configs
@@ -38,11 +36,9 @@ def get_config(config=None):
conf = Config()
base = ['system', 'logs']
- default_values = defaults(base)
- logs_config = conf.get_config_dict(base,
- key_mangling=('-', '_'),
- get_first_key=True)
- logs_config = dict_merge(default_values, logs_config)
+ logs_config = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return logs_config
diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py
index e6c7a0ed2..d92121b3d 100755
--- a/src/conf_mode/system-option.py
+++ b/src/conf_mode/system-option.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,14 +21,12 @@ from sys import exit
from time import sleep
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configverify import verify_source_interface
from vyos.template import render
-from vyos.util import cmd
-from vyos.util import is_systemd_service_running
-from vyos.validate import is_addr_assigned
-from vyos.validate import is_intf_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.network import is_addr_assigned
+from vyos.utils.network import is_intf_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -36,6 +34,11 @@ airbag.enable()
curlrc_config = r'/etc/curlrc'
ssh_config = r'/etc/ssh/ssh_config.d/91-vyos-ssh-client-options.conf'
systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target'
+time_format_to_locale = {
+ '12-hour': 'en_US.UTF-8',
+ '24-hour': 'en_GB.UTF-8'
+}
+
def get_config(config=None):
if config:
@@ -43,12 +46,9 @@ def get_config(config=None):
else:
conf = Config()
base = ['system', 'option']
- options = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
- # 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)
- options = dict_merge(default_values, options)
+ options = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return options
@@ -143,6 +143,11 @@ def apply(options):
else:
cmd('systemctl disable root-partition-auto-resize.service')
+ # Time format 12|24-hour
+ if 'time_format' in options:
+ time_format = time_format_to_locale.get(options['time_format'])
+ cmd(f'localectl set-locale LC_TIME={time_format}')
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py
index cf34bad2e..07fbb0734 100755
--- a/src/conf_mode/system-syslog.py
+++ b/src/conf_mode/system-syslog.py
@@ -19,12 +19,10 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
-from vyos.util import call
+from vyos.utils.process import call
from vyos.template import render
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -46,59 +44,13 @@ def get_config(config=None):
get_first_key=True, no_tag_node_value_mangle=True)
syslog.update({ 'logrotate' : logrotate_conf })
+
tmp = is_node_changed(conf, base + ['vrf'])
if tmp: syslog.update({'restart_required': {}})
- # 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: some syslog default values can not be merged here (originating from
- # a tagNode - remove and add them later per individual tagNode instance
- if 'console' in default_values:
- del default_values['console']
- for entity in ['global', 'user', 'host', 'file']:
- if entity in default_values:
- del default_values[entity]
-
- syslog = dict_merge(default_values, syslog)
-
- # XXX: add defaults for "console" tree
- if 'console' in syslog and 'facility' in syslog['console']:
- default_values = defaults(base + ['console', 'facility'])
- for facility in syslog['console']['facility']:
- syslog['console']['facility'][facility] = dict_merge(default_values,
- syslog['console']['facility'][facility])
-
- # XXX: add defaults for "host" tree
- if 'host' in syslog:
- default_values_host = defaults(base + ['host'])
- if 'facility' in default_values_host:
- del default_values_host['facility']
- default_values_facility = defaults(base + ['host', 'facility'])
-
- for host, host_config in syslog['host'].items():
- syslog['host'][host] = dict_merge(default_values_host, syslog['host'][host])
- if 'facility' in host_config:
- for facility in host_config['facility']:
- syslog['host'][host]['facility'][facility] = dict_merge(default_values_facility,
- syslog['host'][host]['facility'][facility])
-
- # XXX: add defaults for "user" tree
- if 'user' in syslog:
- default_values = defaults(base + ['user', 'facility'])
- for user, user_config in syslog['user'].items():
- if 'facility' in user_config:
- for facility in user_config['facility']:
- syslog['user'][user]['facility'][facility] = dict_merge(default_values,
- syslog['user'][user]['facility'][facility])
-
- # XXX: add defaults for "file" tree
- if 'file' in syslog:
- default_values = defaults(base + ['file'])
- for file, file_config in syslog['file'].items():
- for facility in file_config['facility']:
- syslog['file'][file]['facility'][facility] = dict_merge(default_values,
- syslog['file'][file]['facility'][facility])
+ syslog = conf.merge_defaults(syslog, recursive=True)
+ if syslog.from_defaults(['global']):
+ del syslog['global']
return syslog
diff --git a/src/conf_mode/system-timezone.py b/src/conf_mode/system-timezone.py
index 3d98ba774..cd3d4b229 100755
--- a/src/conf_mode/system-timezone.py
+++ b/src/conf_mode/system-timezone.py
@@ -20,7 +20,7 @@ import os
from copy import deepcopy
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import call
+from vyos.utils.process import call
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py
index e922edc4e..ebf9a113b 100755
--- a/src/conf_mode/system_console.py
+++ b/src/conf_mode/system_console.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -19,12 +19,10 @@ import re
from pathlib import Path
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.util import call
-from vyos.util import read_file
-from vyos.util import write_file
+from vyos.utils.process import call
+from vyos.utils.file import read_file
+from vyos.utils.file import write_file
from vyos.template import render
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -45,16 +43,12 @@ def get_config(config=None):
if 'device' not in console:
return console
- # convert CLI values to system values
- default_values = defaults(base + ['device'])
for device, device_config in console['device'].items():
if 'speed' not in device_config and device.startswith('hvc'):
# XEN console has a different default console speed
console['device'][device]['speed'] = 38400
- else:
- # Merge in XML defaults - the proper way to do it
- console['device'][device] = dict_merge(default_values,
- console['device'][device])
+
+ console = conf.merge_defaults(console, recursive=True)
return console
diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py
index 1af0055f6..fb252238a 100755
--- a/src/conf_mode/system_frr.py
+++ b/src/conf_mode/system_frr.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,7 +22,9 @@ from vyos import airbag
from vyos.config import Config
from vyos.logger import syslog
from vyos.template import render_to_string
-from vyos.util import read_file, write_file, run
+from vyos.utils.file import read_file
+from vyos.utils.file import write_file
+from vyos.utils.process import run
airbag.enable()
# path to daemons config and config status files
diff --git a/src/conf_mode/system_lcd.py b/src/conf_mode/system_lcd.py
index 3341dd738..eb88224d1 100755
--- a/src/conf_mode/system_lcd.py
+++ b/src/conf_mode/system_lcd.py
@@ -19,8 +19,8 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.util import call
-from vyos.util import find_device_file
+from vyos.utils.process import call
+from vyos.utils.system import find_device_file
from vyos.template import render
from vyos import ConfigError
from vyos import airbag
diff --git a/src/conf_mode/system_sflow.py b/src/conf_mode/system_sflow.py
index a0c3fca7f..2df1bbb7a 100755
--- a/src/conf_mode/system_sflow.py
+++ b/src/conf_mode/system_sflow.py
@@ -19,11 +19,9 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.template import render
-from vyos.util import call
-from vyos.validate import is_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -42,26 +40,9 @@ def get_config(config=None):
if not conf.exists(base):
return None
- sflow = conf.get_config_dict(base,
- key_mangling=('-', '_'),
- get_first_key=True)
-
- # 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)
-
- sflow = dict_merge(default_values, sflow)
-
- # Ignore default XML values if config doesn't exists
- # Delete key from dict
- if 'port' in sflow['server']:
- del sflow['server']['port']
-
- # Set default values per server
- if 'server' in sflow:
- for server in sflow['server']:
- default_values = defaults(base + ['server'])
- sflow['server'][server] = dict_merge(default_values, sflow['server'][server])
+ sflow = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return sflow
diff --git a/src/conf_mode/system_sysctl.py b/src/conf_mode/system_sysctl.py
index 2e0004ffa..f6b02023d 100755
--- a/src/conf_mode/system_sysctl.py
+++ b/src/conf_mode/system_sysctl.py
@@ -20,7 +20,7 @@ from sys import exit
from vyos.config import Config
from vyos.template import render
-from vyos.util import cmd
+from vyos.utils.process import cmd
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/system_update_check.py b/src/conf_mode/system_update_check.py
index 08ecfcb81..8d641a97d 100755
--- a/src/conf_mode/system_update_check.py
+++ b/src/conf_mode/system_update_check.py
@@ -22,7 +22,7 @@ from pathlib import Path
from sys import exit
from vyos.config import Config
-from vyos.util import call
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py
index c5daccb7f..3ad346e2e 100755
--- a/src/conf_mode/tftp_server.py
+++ b/src/conf_mode/tftp_server.py
@@ -24,14 +24,12 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configverify import verify_vrf
from vyos.template import render
from vyos.template import is_ipv4
-from vyos.util import call
-from vyos.util import chmod_755
-from vyos.validate import is_addr_assigned
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.permission import chmod_755
+from vyos.utils.network import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -48,11 +46,9 @@ def get_config(config=None):
if not conf.exists(base):
return None
- tftpd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- tftpd = dict_merge(default_values, tftpd)
+ tftpd = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
return tftpd
def verify(tftpd):
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index 63887b278..fa271cbdb 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -27,7 +27,7 @@ from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_interface_exists
-from vyos.configdict import dict_merge
+from vyos.defaults import directories
from vyos.ifconfig import Interface
from vyos.pki import encode_public_key
from vyos.pki import load_private_key
@@ -39,12 +39,11 @@ from vyos.template import ip_from_cidr
from vyos.template import is_ipv4
from vyos.template import is_ipv6
from vyos.template import render
-from vyos.validate import is_ipv6_link_local
-from vyos.util import call
-from vyos.util import dict_search
-from vyos.util import dict_search_args
-from vyos.util import run
-from vyos.xml import defaults
+from vyos.utils.network import is_ipv6_link_local
+from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_args
+from vyos.utils.process import call
+from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -69,7 +68,6 @@ KEY_PATH = f'{swanctl_dir}/private/'
CA_PATH = f'{swanctl_dir}/x509ca/'
CRL_PATH = f'{swanctl_dir}/x509crl/'
-DHCP_BASE = '/var/lib/dhcp/dhclient'
DHCP_HOOK_IFLIST = '/tmp/ipsec_dhcp_waiting'
def get_config(config=None):
@@ -84,88 +82,23 @@ def get_config(config=None):
# retrieve common dictionary keys
ipsec = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base)
- # XXX: T2665: we must safely remove default values for tag nodes, those are
- # added in a more fine grained way later on
- del default_values['esp_group']
- del default_values['ike_group']
- del default_values['remote_access']
- del default_values['site_to_site']
- ipsec = dict_merge(default_values, ipsec)
-
- if 'esp_group' in ipsec:
- default_values = defaults(base + ['esp-group'])
- for group in ipsec['esp_group']:
- ipsec['esp_group'][group] = dict_merge(default_values,
- ipsec['esp_group'][group])
- if 'ike_group' in ipsec:
- default_values = defaults(base + ['ike-group'])
- # proposal is a tag node which may come with individual defaults per node
- if 'proposal' in default_values:
- del default_values['proposal']
-
- for group in ipsec['ike_group']:
- ipsec['ike_group'][group] = dict_merge(default_values,
- ipsec['ike_group'][group])
-
- if 'proposal' in ipsec['ike_group'][group]:
- default_values = defaults(base + ['ike-group', 'proposal'])
- for proposal in ipsec['ike_group'][group]['proposal']:
- ipsec['ike_group'][group]['proposal'][proposal] = dict_merge(default_values,
- ipsec['ike_group'][group]['proposal'][proposal])
-
- # 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 dict_search('remote_access.connection', ipsec):
- default_values = defaults(base + ['remote-access', 'connection'])
- for rw in ipsec['remote_access']['connection']:
- ipsec['remote_access']['connection'][rw] = dict_merge(default_values,
- ipsec['remote_access']['connection'][rw])
-
- # 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 dict_search('remote_access.radius.server', ipsec):
- # Fist handle the "base" stuff like RADIUS timeout
- default_values = defaults(base + ['remote-access', 'radius'])
- if 'server' in default_values:
- del default_values['server']
- ipsec['remote_access']['radius'] = dict_merge(default_values,
- ipsec['remote_access']['radius'])
-
- # Take care about individual RADIUS servers implemented as tagNodes - this
- # requires special treatment
- default_values = defaults(base + ['remote-access', 'radius', 'server'])
- for server in ipsec['remote_access']['radius']['server']:
- ipsec['remote_access']['radius']['server'][server] = dict_merge(default_values,
- ipsec['remote_access']['radius']['server'][server])
-
- # 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 dict_search('site_to_site.peer', ipsec):
- default_values = defaults(base + ['site-to-site', 'peer'])
- for peer in ipsec['site_to_site']['peer']:
- ipsec['site_to_site']['peer'][peer] = dict_merge(default_values,
- ipsec['site_to_site']['peer'][peer])
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
ipsec['dhcp_no_address'] = {}
ipsec['install_routes'] = 'no' if conf.exists(base + ["options", "disable-route-autoinstall"]) else default_install_routes
ipsec['interface_change'] = leaf_node_changed(conf, base + ['interface'])
ipsec['nhrp_exists'] = conf.exists(['protocols', 'nhrp', 'tunnel'])
ipsec['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
+ no_tag_node_value_mangle=True,
+ get_first_key=True)
tmp = conf.get_config_dict(l2tp_base, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
+ no_tag_node_value_mangle=True,
+ get_first_key=True)
if tmp:
- ipsec['l2tp'] = tmp
- l2tp_defaults = defaults(l2tp_base)
- ipsec['l2tp'] = dict_merge(l2tp_defaults, ipsec['l2tp'])
+ ipsec['l2tp'] = conf.merge_defaults(tmp, recursive=True)
ipsec['l2tp_outside_address'] = conf.return_value(['vpn', 'l2tp', 'remote-access', 'outside-address'])
ipsec['l2tp_ike_default'] = 'aes256-sha1-modp1024,3des-sha1-modp1024'
ipsec['l2tp_esp_default'] = 'aes256-sha1,3des-sha1'
@@ -433,8 +366,9 @@ def verify(ipsec):
dhcp_interface = peer_conf['dhcp_interface']
verify_interface_exists(dhcp_interface)
+ dhcp_base = directories['isc_dhclient_dir']
- if not os.path.exists(f'{DHCP_BASE}_{dhcp_interface}.conf'):
+ if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'):
raise ConfigError(f"Invalid dhcp-interface on site-to-site peer {peer}")
address = get_dhcp_address(dhcp_interface)
@@ -455,7 +389,7 @@ def verify(ipsec):
if dict_search('options.disable_route_autoinstall',
ipsec) == None:
- Warning('It\'s recommended to use ipsec vty with the next command\n[set vpn ipsec option disable-route-autoinstall]')
+ Warning('It\'s recommended to use ipsec vti with the next command\n[set vpn ipsec option disable-route-autoinstall]')
if 'bind' in peer_conf['vti']:
vti_interface = peer_conf['vti']['bind']
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index ffac3b023..6232ce64a 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -26,10 +26,10 @@ from ipaddress import ip_network
from vyos.config import Config
from vyos.template import is_ipv4
from vyos.template import render
-from vyos.util import call
-from vyos.util import get_half_cpus
-from vyos.util import check_port_availability
-from vyos.util import is_listen_port_bind_service
+from vyos.utils.process import call
+from vyos.utils.system import get_half_cpus
+from vyos.utils.network import check_port_availability
+from vyos.utils.network import is_listen_port_bind_service
from vyos import ConfigError
from vyos import airbag
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index 83021a3e6..a039172c4 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -19,18 +19,16 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
-from vyos.util import call
-from vyos.util import check_port_availability
-from vyos.util import is_systemd_service_running
-from vyos.util import is_listen_port_bind_service
-from vyos.util import dict_search
-from vyos.xml import defaults
+from vyos.utils.process import call
+from vyos.utils.network import check_port_availability
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.network import is_listen_port_bind_service
+from vyos.utils.dict import dict_search
from vyos import ConfigError
-from crypt import crypt, mksalt, METHOD_SHA512
+from passlib.hash import sha512_crypt
from time import sleep
from vyos import airbag
@@ -45,66 +43,7 @@ radius_servers = cfg_dir + '/radius_servers'
# Generate hash from user cleartext password
def get_hash(password):
- return crypt(password, mksalt(METHOD_SHA512))
-
-
-def _default_dict_cleanup(origin: dict, default_values: dict) -> dict:
- """
- https://vyos.dev/T2665
- Clear unnecessary key values in merged config by dict_merge function
- :param origin: config
- :type origin: dict
- :param default_values: default values
- :type default_values: dict
- :return: merged dict
- :rtype: dict
- """
- if 'mode' in origin["authentication"] and "local" in \
- origin["authentication"]["mode"]:
- del origin['authentication']['local_users']['username']['otp']
- if not origin["authentication"]["local_users"]["username"]:
- raise ConfigError(
- 'Openconnect authentication mode local requires at least one user')
- default_ocserv_usr_values = \
- default_values['authentication']['local_users']['username']['otp']
- for user, params in origin['authentication']['local_users'][
- 'username'].items():
- # Not every configuration requires OTP settings
- if origin['authentication']['local_users']['username'][user].get(
- 'otp'):
- origin['authentication']['local_users']['username'][user][
- 'otp'] = dict_merge(default_ocserv_usr_values,
- origin['authentication'][
- 'local_users']['username'][user][
- 'otp'])
-
- if 'mode' in origin["authentication"] and "radius" in \
- origin["authentication"]["mode"]:
- del origin['authentication']['radius']['server']['port']
- if not origin["authentication"]['radius']['server']:
- raise ConfigError(
- 'Openconnect authentication mode radius requires at least one RADIUS server')
- default_values_radius_port = \
- default_values['authentication']['radius']['server']['port']
- for server, params in origin['authentication']['radius'][
- 'server'].items():
- if 'port' not in params:
- params['port'] = default_values_radius_port
-
- if 'mode' in origin["accounting"] and "radius" in \
- origin["accounting"]["mode"]:
- del origin['accounting']['radius']['server']['port']
- if not origin["accounting"]['radius']['server']:
- raise ConfigError(
- 'Openconnect accounting mode radius requires at least one RADIUS server')
- default_values_radius_port = \
- default_values['accounting']['radius']['server']['port']
- for server, params in origin['accounting']['radius'][
- 'server'].items():
- if 'port' not in params:
- params['port'] = default_values_radius_port
- return origin
-
+ return sha512_crypt.hash(password)
def get_config(config=None):
if config:
@@ -115,16 +54,14 @@ def get_config(config=None):
if not conf.exists(base):
return None
- ocserv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- ocserv = dict_merge(default_values, ocserv)
- # workaround a "know limitation" - https://vyos.dev/T2665
- ocserv = _default_dict_cleanup(ocserv, default_values)
+ ocserv = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
+
if ocserv:
ocserv['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
+ no_tag_node_value_mangle=True,
+ get_first_key=True)
return ocserv
@@ -141,6 +78,8 @@ def verify(ocserv):
# Check accounting
if "accounting" in ocserv:
if "mode" in ocserv["accounting"] and "radius" in ocserv["accounting"]["mode"]:
+ if not origin["accounting"]['radius']['server']:
+ raise ConfigError('Openconnect accounting mode radius requires at least one RADIUS server')
if "authentication" not in ocserv or "mode" not in ocserv["authentication"]:
raise ConfigError('Accounting depends on OpenConnect authentication configuration')
elif "radius" not in ocserv["authentication"]["mode"]:
@@ -149,9 +88,13 @@ def verify(ocserv):
# Check authentication
if "authentication" in ocserv:
if "mode" in ocserv["authentication"]:
- if "local" in ocserv["authentication"]["mode"]:
- if "radius" in ocserv["authentication"]["mode"]:
+ if ("local" in ocserv["authentication"]["mode"] and
+ "radius" in ocserv["authentication"]["mode"]):
raise ConfigError('OpenConnect authentication modes are mutually-exclusive, remove either local or radius from your configuration')
+ if "radius" in ocserv["authentication"]["mode"]:
+ if not ocserv["authentication"]['radius']['server']:
+ raise ConfigError('Openconnect authentication mode radius requires at least one RADIUS server')
+ if "local" in ocserv["authentication"]["mode"]:
if not ocserv["authentication"]["local_users"]:
raise ConfigError('openconnect mode local required at least one user')
if not ocserv["authentication"]["local_users"]["username"]:
diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py
index b9d18110a..d542f57fe 100755
--- a/src/conf_mode/vpn_pptp.py
+++ b/src/conf_mode/vpn_pptp.py
@@ -23,7 +23,8 @@ from sys import exit
from vyos.config import Config
from vyos.template import render
-from vyos.util import call, get_half_cpus
+from vyos.utils.system import get_half_cpus
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 2949ab290..e98d8385b 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -25,11 +25,11 @@ from vyos.configverify import verify_accel_ppp_base_service
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
-from vyos.util import call
-from vyos.util import check_port_availability
-from vyos.util import dict_search
-from vyos.util import is_listen_port_bind_service
-from vyos.util import write_file
+from vyos.utils.process import call
+from vyos.utils.network import check_port_availability
+from vyos.utils.dict import dict_search
+from vyos.utils.network import is_listen_port_bind_service
+from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
new file mode 100755
index 000000000..82c2f236e
--- /dev/null
+++ b/src/conf_mode/vpp.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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 psutil import virtual_memory
+
+from pathlib import Path
+from re import search as re_search, MULTILINE as re_M
+
+from vyos.config import Config
+from vyos.configdep import set_dependents, call_dependents
+from vyos.configdict import node_changed
+from vyos.ifconfig import Section
+from vyos.utils.boot import boot_configuration_complete
+from vyos.utils.process import call
+from vyos.utils.process import rc_cmd
+from vyos.utils.system import sysctl_read
+from vyos.utils.system import sysctl_apply
+from vyos.template import render
+
+from vyos import ConfigError
+from vyos import airbag
+from vyos.vpp import VPPControl
+from vyos.vpp import HostControl
+
+airbag.enable()
+
+service_name = 'vpp'
+service_conf = Path(f'/run/vpp/{service_name}.conf')
+systemd_override = '/run/systemd/system/vpp.service.d/10-override.conf'
+
+# Free memory required for VPP
+# 2 GB for hugepages + 1 GB for other services
+MIN_AVAILABLE_MEMORY: int = 3 * 1024**3
+
+
+def _get_pci_address_by_interface(iface) -> str:
+ rc, out = rc_cmd(f'ethtool -i {iface}')
+ # if ethtool command was successful
+ if rc == 0 and out:
+ regex_filter = r'^bus-info: (?P<address>\w+:\w+:\w+\.\w+)$'
+ re_obj = re_search(regex_filter, out, re_M)
+ # if bus-info with PCI address found
+ if re_obj:
+ address = re_obj.groupdict().get('address', '')
+ return address
+ # use VPP - maybe interface already attached to it
+ vpp_control = VPPControl(attempts=20, interval=500)
+ pci_addr = vpp_control.get_pci_addr(iface)
+ if pci_addr:
+ return pci_addr
+ # raise error if PCI address was not found
+ raise ConfigError(f'Cannot find PCI address for interface {iface}')
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['vpp']
+ base_ethernet = ['interfaces', 'ethernet']
+
+ # find interfaces removed from VPP
+ removed_ifaces = []
+ tmp = node_changed(conf, base + ['interface'])
+ if tmp:
+ for removed_iface in tmp:
+ pci_address: str = _get_pci_address_by_interface(removed_iface)
+ removed_ifaces.append({
+ 'iface_name': removed_iface,
+ 'iface_pci_addr': pci_address
+ })
+ # add an interface to a list of interfaces that need
+ # to be reinitialized after the commit
+ set_dependents('ethernet', conf, removed_iface)
+
+ if not conf.exists(base):
+ return {'removed_ifaces': removed_ifaces}
+
+ config = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
+
+ if 'interface' in config:
+ for iface, iface_config in config['interface'].items():
+ # add an interface to a list of interfaces that need
+ # to be reinitialized after the commit
+ set_dependents('ethernet', conf, iface)
+
+ # Get PCI address auto
+ if iface_config['pci'] == 'auto':
+ config['interface'][iface]['pci'] = _get_pci_address_by_interface(iface)
+
+ config['other_interfaces'] = conf.get_config_dict(base_ethernet, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+
+ if removed_ifaces:
+ config['removed_ifaces'] = removed_ifaces
+
+ return config
+
+
+def verify(config):
+ # bail out early - looks like removal from running config
+ if not config or (len(config) == 1 and 'removed_ifaces' in config):
+ return None
+
+ if 'interface' not in config:
+ raise ConfigError('"interface" is required but not set!')
+
+ if 'cpu' in config:
+ if 'corelist_workers' in config['cpu'] and 'main_core' not in config[
+ 'cpu']:
+ raise ConfigError('"cpu main-core" is required but not set!')
+
+ memory_available: int = virtual_memory().available
+ if memory_available < MIN_AVAILABLE_MEMORY:
+ raise ConfigError(
+ 'Not enough free memory to start VPP:\n'
+ f'available: {round(memory_available / 1024**3, 1)}GB\n'
+ f'required: {round(MIN_AVAILABLE_MEMORY / 1024**3, 1)}GB')
+
+
+def generate(config):
+ if not config or (len(config) == 1 and 'removed_ifaces' in config):
+ # Remove old config and return
+ service_conf.unlink(missing_ok=True)
+ return None
+
+ render(service_conf, 'vpp/startup.conf.j2', config)
+ render(systemd_override, 'vpp/override.conf.j2', config)
+
+ # apply default sysctl values from
+ # https://github.com/FDio/vpp/blob/v23.06/src/vpp/conf/80-vpp.conf
+ sysctl_config: dict[str, str] = {
+ 'vm.nr_hugepages': '1024',
+ 'vm.max_map_count': '3096',
+ 'vm.hugetlb_shm_group': '0',
+ 'kernel.shmmax': '2147483648'
+ }
+ # we do not want to reduce `kernel.shmmax`
+ kernel_shmnax_current: str = sysctl_read('kernel.shmmax')
+ if int(kernel_shmnax_current) > int(sysctl_config['kernel.shmmax']):
+ sysctl_config['kernel.shmmax'] = kernel_shmnax_current
+
+ if not sysctl_apply(sysctl_config):
+ raise ConfigError('Cannot configure sysctl parameters for VPP')
+
+ return None
+
+
+def apply(config):
+ if not config or (len(config) == 1 and 'removed_ifaces' in config):
+ call(f'systemctl stop {service_name}.service')
+ else:
+ call('systemctl daemon-reload')
+ call(f'systemctl restart {service_name}.service')
+
+ # Initialize interfaces removed from VPP
+ for iface in config.get('removed_ifaces', []):
+ host_control = HostControl()
+ # rescan PCI to use a proper driver
+ host_control.pci_rescan(iface['iface_pci_addr'])
+ # rename to the proper name
+ iface_new_name: str = host_control.get_eth_name(iface['iface_pci_addr'])
+ host_control.rename_iface(iface_new_name, iface['iface_name'])
+
+ if 'interface' in config:
+ # connect to VPP
+ # must be performed multiple attempts because API is not available
+ # immediately after the service restart
+ vpp_control = VPPControl(attempts=20, interval=500)
+ for iface, _ in config['interface'].items():
+ # Create lcp
+ if iface not in Section.interfaces():
+ vpp_control.lcp_pair_add(iface, iface)
+
+ # reinitialize interfaces, but not during the first boot
+ if boot_configuration_complete():
+ call_dependents()
+
+
+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/vrf.py b/src/conf_mode/vrf.py
index 0b983293e..37625142c 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -26,13 +26,15 @@ from vyos.configverify import verify_route_map
from vyos.ifconfig import Interface
from vyos.template import render
from vyos.template import render_to_string
-from vyos.util import call
-from vyos.util import cmd
-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.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import get_vrf_members
+from vyos.utils.network import interface_exists
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.process import popen
+from vyos.utils.process import run
+from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -143,7 +145,7 @@ def verify(vrf):
raise ConfigError(f'VRF "{name}" table id is mandatory!')
# routing table id can't be changed - OS restriction
- if os.path.isdir(f'/sys/class/net/{name}'):
+ if interface_exists(name):
tmp = str(dict_search('linkinfo.info_data.table', get_interface_config(name)))
if tmp and tmp != vrf_config['table']:
raise ConfigError(f'VRF "{name}" table id modification not possible!')
@@ -195,12 +197,23 @@ def apply(vrf):
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 link delete dev {tmp}')
+ if interface_exists(tmp):
+ # T5492: deleting a VRF instance may leafe processes running
+ # (e.g. dhclient) as there is a depedency ordering issue in the CLI.
+ # We need to ensure that we stop the dhclient processes first so
+ # a proper DHCLP RELEASE message is sent
+ for interface in get_vrf_members(tmp):
+ vrf_iface = Interface(interface)
+ vrf_iface.set_dhcp(False)
+ vrf_iface.set_dhcpv6(False)
+
# Remove nftables conntrack zone map item
nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}'
cmd(f'nft {nft_del_element}')
+ # Delete the VRF Kernel interface
+ call(f'ip link delete dev {tmp}')
+
if 'name' in vrf:
# Separate VRFs in conntrack table
# check if table already exists
@@ -245,7 +258,7 @@ def apply(vrf):
for name, config in vrf['name'].items():
table = config['table']
- if not os.path.isdir(f'/sys/class/net/{name}'):
+ if not interface_exists(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}')
diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py
index 9f33536e5..23b341079 100644
--- a/src/conf_mode/vrf_vni.py
+++ b/src/conf_mode/vrf_vni.py
@@ -19,7 +19,7 @@ from sys import exit
from vyos.config import Config
from vyos.template import render_to_string
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import frr
from vyos import airbag
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook
index 49bb18372..35721d009 100644
--- a/src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook
@@ -28,7 +28,8 @@ if [[ $reason =~ ^(REBOOT6|INIT6|EXPIRE6|RELEASE6|STOP6|INFORM6|BOUND6|REBIND6|D
fi
if [ "$RUN" = "yes" ]; then
- LOG=/var/lib/dhcp/dhclient_"$interface"."$proto"lease
+ BASE_PATH=$(python3 -c "from vyos.defaults import directories; print(directories['isc_dhclient_dir'])")
+ LOG=${BASE_PATH}/dhclient_"$interface"."$proto"lease
echo `date` > $LOG
for i in reason interface new_expiry new_dhcp_lease_time medium \
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook
index 1f1926e17..c7a92fe26 100755
--- a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if [ "$reason" == "REBOOT" ] || [ "$reason" == "EXPIRE" ]; then
- exit 0
+ return 0
fi
DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_waiting"
@@ -24,22 +24,22 @@ if [ -f $DHCP_HOOK_IFLIST ] && [ "$reason" == "BOUND" ]; then
if grep -qw $interface $DHCP_HOOK_IFLIST; then
sudo rm $DHCP_HOOK_IFLIST
sudo /usr/libexec/vyos/conf_mode/vpn_ipsec.py
- exit 0
+ return 0
fi
fi
if [ "$old_ip_address" == "$new_ip_address" ] && [ "$reason" == "BOUND" ]; then
- exit 0
+ return 0
fi
python3 - <<PYEND
import os
import re
-from vyos.util import call
-from vyos.util import cmd
-from vyos.util import read_file
-from vyos.util import write_file
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.file import read_file
+from vyos.utils.file import write_file
SWANCTL_CONF="/etc/swanctl/swanctl.conf"
@@ -83,4 +83,4 @@ if __name__ == '__main__':
call('sudo swanctl -q')
exit(0)
-PYEND \ No newline at end of file
+PYEND
diff --git a/src/etc/ipsec.d/vti-up-down b/src/etc/ipsec.d/vti-up-down
index 1ffb32955..9eb6fac48 100755
--- a/src/etc/ipsec.d/vti-up-down
+++ b/src/etc/ipsec.d/vti-up-down
@@ -25,9 +25,9 @@ from syslog import LOG_PID
from syslog import LOG_INFO
from vyos.configquery import ConfigTreeQuery
-from vyos.util import call
-from vyos.util import get_interface_config
-from vyos.util import get_interface_address
+from vyos.utils.process import call
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import get_interface_address
if __name__ == '__main__':
verb = os.getenv('PLUTO_VERB')
diff --git a/src/etc/modprobe.d/openvpn.conf b/src/etc/modprobe.d/openvpn.conf
new file mode 100644
index 000000000..a9259fea2
--- /dev/null
+++ b/src/etc/modprobe.d/openvpn.conf
@@ -0,0 +1 @@
+blacklist ovpn-dco-v2
diff --git a/src/etc/netplug/linkdown.d/dhclient b/src/etc/netplug/linkdown.d/dhclient
deleted file mode 100755
index 555ff9134..000000000
--- a/src/etc/netplug/linkdown.d/dhclient
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/perl
-#
-# Module: dhclient
-#
-# **** License ****
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 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.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'.
-# You can also obtain it by writing to the Free Software Foundation,
-# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
-# MA 02110-1301, USA.
-#
-# This code was originally developed by Vyatta, Inc.
-# Portions created by Vyatta are Copyright (C) 2008 Vyatta, Inc.
-# All Rights Reserved.
-#
-# Author: Mohit Mehta
-# Date: November 2008
-# Description: Script to release lease on link down
-#
-# **** End License ****
-#
-
-use lib "/opt/vyatta/share/perl5/";
-use Vyatta::Config;
-use Vyatta::Misc;
-
-use strict;
-use warnings;
-
-sub stop_dhclient {
- my $intf = shift;
- my $dhcp_daemon = '/sbin/dhclient';
- my ($intf_config_file, $intf_process_id_file, $intf_leases_file) = Vyatta::Misc::generate_dhclient_intf_files($intf);
- my $release_cmd = "sudo $dhcp_daemon -q -cf $intf_config_file -pf $intf_process_id_file -lf $intf_leases_file -r $intf 2> /dev/null;";
- $release_cmd .= "sudo rm -f $intf_process_id_file 2> /dev/null";
- system ($release_cmd);
-}
-
-
-#
-# main
-#
-
-my $dev=shift;
-
-# only do this if interface is configured to use dhcp for getting IP address
-if (Vyatta::Misc::is_dhcp_enabled($dev, "outside_cli")) {
- # do a dhcp lease release for interface
- stop_dhclient($dev);
-}
-
-exit 0;
-
-# end of file
-
diff --git a/src/etc/netplug/linkup.d/dhclient b/src/etc/netplug/linkup.d/dhclient
deleted file mode 100755
index 8e50715fd..000000000
--- a/src/etc/netplug/linkup.d/dhclient
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/perl
-#
-# Module: dhclient
-#
-# **** License ****
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 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.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'.
-# You can also obtain it by writing to the Free Software Foundation,
-# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
-# MA 02110-1301, USA.
-#
-# This code was originally developed by Vyatta, Inc.
-# Portions created by Vyatta are Copyright (C) 2008 Vyatta, Inc.
-# All Rights Reserved.
-#
-# Author: Mohit Mehta
-# Date: November 2008
-# Description: Script to renew lease on link up
-#
-# **** End License ****
-#
-
-use lib "/opt/vyatta/share/perl5/";
-use Vyatta::Config;
-use Vyatta::Misc;
-
-use strict;
-use warnings;
-
-sub run_dhclient {
- my $intf = shift;
- my $dhcp_daemon = '/sbin/dhclient';
- my ($intf_config_file, $intf_process_id_file, $intf_leases_file) = Vyatta::Misc::generate_dhclient_intf_files($intf);
- my $cmd = "sudo $dhcp_daemon -pf $intf_process_id_file -x $intf 2> /dev/null; sudo rm -f $intf_process_id_file 2> /dev/null;";
- $cmd .= "sudo $dhcp_daemon -q -nw -cf $intf_config_file -pf $intf_process_id_file -lf $intf_leases_file $intf 2> /dev/null &";
- system ($cmd);
-}
-
-#
-# main
-#
-
-my $dev=shift;
-
-# only do this if interface is configured to use dhcp for getting IP address
-if (Vyatta::Misc::is_dhcp_enabled($dev, "outside_cli")) {
- # do a dhcp lease renew for interface
- run_dhclient($dev);
-}
-
-exit 0;
-
-# end of file
-
diff --git a/src/etc/netplug/linkup.d/vyos-python-helper b/src/etc/netplug/linkup.d/vyos-python-helper
new file mode 100755
index 000000000..9c59c58ad
--- /dev/null
+++ b/src/etc/netplug/linkup.d/vyos-python-helper
@@ -0,0 +1,4 @@
+#!/bin/sh
+PYTHON3=$(which python3)
+# Call the real python script and forward commandline arguments
+$PYTHON3 /etc/netplug/vyos-netplug-dhcp-client "${@:1}"
diff --git a/src/etc/netplug/netplug b/src/etc/netplug/netplug
new file mode 100755
index 000000000..60b65e8c9
--- /dev/null
+++ b/src/etc/netplug/netplug
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+dev="$1"
+action="$2"
+
+case "$action" in
+in)
+ run-parts --arg $dev --arg in /etc/netplug/linkup.d
+ ;;
+out)
+ run-parts --arg $dev --arg out /etc/netplug/linkdown.d
+ ;;
+
+# probe loads and initialises the driver for the interface and brings the
+# interface into the "up" state, so that it can generate netlink(7) events.
+# This interferes with "admin down" for an interface. Thus, commented out. An
+# "admin up" is treated as a "link up" and thus, "link up" action is executed.
+# To execute "link down" action on "admin down", run appropriate script in
+# /etc/netplug/linkdown.d
+#probe)
+# ;;
+
+*)
+ exit 1
+ ;;
+esac
diff --git a/src/etc/netplug/netplugd.conf b/src/etc/netplug/netplugd.conf
new file mode 100644
index 000000000..7da3c67e8
--- /dev/null
+++ b/src/etc/netplug/netplugd.conf
@@ -0,0 +1,4 @@
+eth*
+br*
+bond*
+wlan*
diff --git a/src/etc/netplug/vyos-netplug-dhcp-client b/src/etc/netplug/vyos-netplug-dhcp-client
new file mode 100755
index 000000000..55d15a163
--- /dev/null
+++ b/src/etc/netplug/vyos-netplug-dhcp-client
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+#
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+
+from time import sleep
+
+from vyos.configquery import ConfigTreeQuery
+from vyos.ifconfig import Section
+from vyos.utils.boot import boot_configuration_complete
+from vyos.utils.commit import commit_in_progress
+from vyos.utils.process import call
+from vyos import airbag
+airbag.enable()
+
+if len(sys.argv) < 3:
+ airbag.noteworthy("Must specify both interface and link status!")
+ sys.exit(1)
+
+if not boot_configuration_complete():
+ airbag.noteworthy("System bootup not yet finished...")
+ sys.exit(1)
+
+while commit_in_progress():
+ sleep(1)
+
+interface = sys.argv[1]
+in_out = sys.argv[2]
+config = ConfigTreeQuery()
+
+interface_path = ['interfaces'] + Section.get_config_path(interface).split()
+
+for _, interface_config in config.get_config_dict(interface_path).items():
+ # Bail out early if we do not have an IP address configured
+ if 'address' not in interface_config:
+ continue
+ # Bail out early if interface ist administrative down
+ if 'disable' in interface_config:
+ continue
+ systemd_action = 'start'
+ if in_out == 'out':
+ systemd_action = 'stop'
+ # Start/Stop DHCP service
+ if 'dhcp' in interface_config['address']:
+ call(f'systemctl {systemd_action} dhclient@{interface}.service')
+ # Start/Stop DHCPv6 service
+ if 'dhcpv6' in interface_config['address']:
+ call(f'systemctl {systemd_action} dhcp6c@{interface}.service')
diff --git a/src/etc/opennhrp/opennhrp-script.py b/src/etc/opennhrp/opennhrp-script.py
index 688c7af2a..f6f6d075c 100755
--- a/src/etc/opennhrp/opennhrp-script.py
+++ b/src/etc/opennhrp/opennhrp-script.py
@@ -23,8 +23,8 @@ from json import loads
from pathlib import Path
from vyos.logger import getLogger
-from vyos.util import cmd
-from vyos.util import process_named_running
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
NHRP_CONFIG: str = '/run/opennhrp/opennhrp.conf'
diff --git a/src/etc/rsyslog.conf b/src/etc/rsyslog.conf
index c28e9b537..9781f0835 100644
--- a/src/etc/rsyslog.conf
+++ b/src/etc/rsyslog.conf
@@ -52,8 +52,8 @@ $Umask 0022
#
# Stop excessive logging of sudo
#
-:msg, contains, " pam_unix(sudo:session): session opened for user root(uid=0) by" ~
-:msg, contains, "pam_unix(sudo:session): session closed for user root" ~
+:msg, contains, " pam_unix(sudo:session): session opened for user root(uid=0) by" stop
+:msg, contains, "pam_unix(sudo:session): session closed for user root" stop
#
# Include all config files in /etc/rsyslog.d/
diff --git a/src/etc/systemd/system-generators/vyos-generator b/src/etc/systemd/system-generators/vyos-generator
new file mode 100755
index 000000000..34faab6a2
--- /dev/null
+++ b/src/etc/systemd/system-generators/vyos-generator
@@ -0,0 +1,94 @@
+#!/bin/sh
+set -f
+
+LOG=""
+DEBUG_LEVEL=1
+LOG_D="/run/vyos-router"
+ENABLE="enabled"
+DISABLE="disabled"
+FOUND="found"
+NOTFOUND="notfound"
+RUN_ENABLED_FILE="$LOG_D/$ENABLE"
+VYOS_SYSTEM_TARGET="/lib/systemd/system/vyos.target"
+VYOS_TARGET_NAME="vyos.target"
+
+debug() {
+ local lvl="$1"
+ shift
+ [ "$lvl" -gt "$DEBUG_LEVEL" ] && return
+ if [ -z "$LOG" ]; then
+ local log="$LOG_D/${0##*/}.log"
+ { [ -d "$LOG_D" ] || mkdir -p "$LOG_D"; } &&
+ { : > "$log"; } >/dev/null 2>&1 && LOG="$log" ||
+ LOG="/dev/kmsg"
+ fi
+ echo "$@" >> "$LOG"
+}
+
+default() {
+ _RET="$ENABLE"
+}
+
+main() {
+ local normal_d="$1" early_d="$2" late_d="$3"
+ local target_name="multi-user.target" gen_d="$early_d"
+ local link_path="$gen_d/${target_name}.wants/${VYOS_TARGET_NAME}"
+ local ds="$NOTFOUND"
+
+ debug 1 "$0 normal=$normal_d early=$early_d late=$late_d"
+ debug 2 "$0 $*"
+
+ local search result="error" ret=""
+ for search in default; do
+ if $search; then
+ debug 1 "$search found $_RET"
+ [ "$_RET" = "$ENABLE" -o "$_RET" = "$DISABLE" ] &&
+ result=$_RET && break
+ else
+ ret=$?
+ debug 0 "search $search returned $ret"
+ fi
+ done
+
+ # enable AND ds=found == enable
+ # enable AND ds=notfound == disable
+ # disable || <any> == disabled
+ if [ "$result" = "$ENABLE" ]; then
+ if [ -e "$link_path" ]; then
+ debug 1 "already enabled: no change needed"
+ else
+ [ -d "${link_path%/*}" ] || mkdir -p "${link_path%/*}" ||
+ debug 0 "failed to make dir $link_path"
+ if ln -snf "$VYOS_SYSTEM_TARGET" "$link_path"; then
+ debug 1 "enabled via $link_path -> $VYOS_SYSTEM_TARGET"
+ else
+ ret=$?
+ debug 0 "[$ret] enable failed:" \
+ "ln $VYOS_SYSTEM_TARGET $link_path"
+ fi
+ fi
+ : > "$RUN_ENABLED_FILE"
+ elif [ "$result" = "$DISABLE" ]; then
+ if [ -f "$link_path" ]; then
+ if rm -f "$link_path"; then
+ debug 1 "disabled. removed existing $link_path"
+ else
+ ret=$?
+ debug 0 "[$ret] disable failed, remove $link_path"
+ fi
+ else
+ debug 1 "already disabled: no change needed [no $link_path]"
+ fi
+ if [ -e "$RUN_ENABLED_FILE" ]; then
+ rm -f "$RUN_ENABLED_FILE"
+ fi
+ else
+ debug 0 "unexpected result '$result' 'ds=$ds'"
+ ret=3
+ fi
+ return $ret
+}
+
+main "$@"
+
+# vi: ts=4 expandtab
diff --git a/src/etc/systemd/system/ddclient.service.d/override.conf b/src/etc/systemd/system/ddclient.service.d/override.conf
deleted file mode 100644
index 09d929d39..000000000
--- a/src/etc/systemd/system/ddclient.service.d/override.conf
+++ /dev/null
@@ -1,11 +0,0 @@
-[Unit]
-After=
-After=vyos-router.service
-
-[Service]
-WorkingDirectory=
-WorkingDirectory=/run/ddclient
-PIDFile=
-PIDFile=/run/ddclient/ddclient.pid
-ExecStart=
-ExecStart=/usr/bin/ddclient -cache /run/ddclient/ddclient.cache -pid /run/ddclient/ddclient.pid -file /run/ddclient/ddclient.conf
diff --git a/src/etc/systemd/system/frr.service.d/override.conf b/src/etc/systemd/system/frr.service.d/override.conf
index 2e4b6e295..094f83551 100644
--- a/src/etc/systemd/system/frr.service.d/override.conf
+++ b/src/etc/systemd/system/frr.service.d/override.conf
@@ -1,7 +1,3 @@
-[Unit]
-Before=
-Before=vyos-router.service
-
[Service]
LimitNOFILE=4096
ExecStartPre=/bin/bash -c 'mkdir -p /run/frr/config; \
diff --git a/src/etc/systemd/system/getty@.service.d/aftervyos.conf b/src/etc/systemd/system/getty@.service.d/aftervyos.conf
new file mode 100644
index 000000000..c5753900e
--- /dev/null
+++ b/src/etc/systemd/system/getty@.service.d/aftervyos.conf
@@ -0,0 +1,3 @@
+[Service]
+ExecStartPre=-/usr/libexec/vyos/init/vyos-config
+StandardOutput=journal+console
diff --git a/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf b/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf
new file mode 100644
index 000000000..8ba42778d
--- /dev/null
+++ b/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf
@@ -0,0 +1,3 @@
+[Service]
+ExecStartPre=-/usr/libexec/vyos/init/vyos-config SERIAL
+StandardOutput=journal+console
diff --git a/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
index d7eca5894..bb7515a90 100755
--- a/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
+++ b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
@@ -4,7 +4,7 @@ import json
import re
import time
-from vyos.util import cmd
+from vyos.utils.process import cmd
def get_nft_filter_chains():
diff --git a/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py b/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py
index df4eed131..00f2f184c 100755
--- a/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py
+++ b/src/etc/telegraf/custom_scripts/vyos_services_input_filter.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,7 +17,8 @@
import time
from vyos.configquery import ConfigTreeQuery
-from vyos.util import is_systemd_service_running, process_named_running
+from vyos.utils.process import is_systemd_service_running
+from vyos.utils.process import process_named_running
# Availible services and prouceses
# 1 - service
diff --git a/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py b/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py
index 4e7fb117c..7da57bca8 100755
--- a/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py
+++ b/src/etc/vmware-tools/scripts/resume-vm-default.d/ether-resume.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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,9 +17,9 @@
import sys
import syslog
-from vyos.config import Config
from vyos import ConfigError
-from vyos.util import run
+from vyos.config import Config
+from vyos.utils.process import run
def get_config():
c = Config()
diff --git a/src/helpers/commit-confirm-notify.py b/src/helpers/commit-confirm-notify.py
index eb7859ffa..8d7626c78 100755
--- a/src/helpers/commit-confirm-notify.py
+++ b/src/helpers/commit-confirm-notify.py
@@ -17,6 +17,7 @@ def notify(interval):
if __name__ == "__main__":
# Must be run as root to call wall(1) without a banner.
if len(sys.argv) != 2 or os.getuid() != 0:
+ print('This script requires superuser privileges.', file=sys.stderr)
exit(1)
minutes = int(sys.argv[1])
# Drop the argument from the list so that the notification
diff --git a/src/helpers/run-config-migration.py b/src/helpers/run-config-migration.py
index cc7166c22..ce647ad0a 100755
--- a/src/helpers/run-config-migration.py
+++ b/src/helpers/run-config-migration.py
@@ -20,7 +20,7 @@ import sys
import argparse
import datetime
-from vyos.util import cmd
+from vyos.utils.process import cmd
from vyos.migrator import Migrator, VirtualMigrator
def main():
diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py
index b9cc87bfa..01b06526d 100755
--- a/src/helpers/vyos-boot-config-loader.py
+++ b/src/helpers/vyos-boot-config-loader.py
@@ -26,7 +26,7 @@ from datetime import datetime
from vyos.defaults import directories, config_status
from vyos.configsession import ConfigSession, ConfigSessionError
from vyos.configtree import ConfigTree
-from vyos.util import cmd
+from vyos.utils.process import cmd
STATUS_FILE = config_status
TRACE_FILE = '/tmp/boot-config-trace'
diff --git a/src/helpers/vyos-check-wwan.py b/src/helpers/vyos-check-wwan.py
index 2ff9a574f..334f08dd3 100755
--- a/src/helpers/vyos-check-wwan.py
+++ b/src/helpers/vyos-check-wwan.py
@@ -17,7 +17,7 @@
from vyos.configquery import VbashOpRun
from vyos.configquery import ConfigTreeQuery
-from vyos.util import is_wwan_connected
+from vyos.utils.network import is_wwan_connected
conf = ConfigTreeQuery()
dict = conf.get_config_dict(['interfaces', 'wwan'], key_mangling=('-', '_'),
diff --git a/src/helpers/vyos-domain-resolver.py b/src/helpers/vyos-domain-resolver.py
index e31d9238e..7e2fe2462 100755
--- a/src/helpers/vyos-domain-resolver.py
+++ b/src/helpers/vyos-domain-resolver.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,11 +22,11 @@ from vyos.configdict import dict_merge
from vyos.configquery import ConfigTreeQuery
from vyos.firewall import fqdn_config_parse
from vyos.firewall import fqdn_resolve
-from vyos.util import cmd
-from vyos.util import commit_in_progress
-from vyos.util import dict_search_args
-from vyos.util import run
-from vyos.xml import defaults
+from vyos.utils.commit import commit_in_progress
+from vyos.utils.dict import dict_search_args
+from vyos.utils.process import cmd
+from vyos.utils.process import run
+from vyos.xml_ref import get_defaults
base = ['firewall']
timeout = 300
@@ -49,13 +49,7 @@ def get_config(conf):
firewall = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
no_tag_node_value_mangle=True)
- default_values = defaults(base)
- for tmp in ['name', 'ipv6_name']:
- if tmp in default_values:
- del default_values[tmp]
-
- if 'zone' in default_values:
- del default_values['zone']
+ default_values = get_defaults(base, get_first_key=True)
firewall = dict_merge(default_values, firewall)
diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py
index ce4cf8fa4..cc7610370 100755
--- a/src/helpers/vyos-failover.py
+++ b/src/helpers/vyos-failover.py
@@ -20,7 +20,7 @@ import subprocess
import socket
import time
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
from pathlib import Path
from systemd import journal
diff --git a/src/helpers/vyos-interface-rescan.py b/src/helpers/vyos-interface-rescan.py
index 1ac1810e0..012357259 100755
--- a/src/helpers/vyos-interface-rescan.py
+++ b/src/helpers/vyos-interface-rescan.py
@@ -24,7 +24,7 @@ import netaddr
from vyos.configtree import ConfigTree
from vyos.defaults import directories
-from vyos.util import get_cfg_group_id
+from vyos.utils.permission import get_cfg_group_id
debug = False
diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py
index 14df2734b..8997705fe 100755
--- a/src/helpers/vyos-merge-config.py
+++ b/src/helpers/vyos-merge-config.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -20,11 +20,12 @@ import os
import tempfile
import vyos.defaults
import vyos.remote
+
from vyos.config import Config
from vyos.configtree import ConfigTree
from vyos.migrator import Migrator, VirtualMigrator
-from vyos.util import cmd, DEVNULL
-
+from vyos.utils.process import cmd
+from vyos.utils.process import DEVNULL
if (len(sys.argv) < 2):
print("Need config file name to merge.")
diff --git a/src/helpers/vyos-save-config.py b/src/helpers/vyos-save-config.py
new file mode 100755
index 000000000..2812155e8
--- /dev/null
+++ b/src/helpers/vyos-save-config.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+import os
+import re
+import sys
+from tempfile import NamedTemporaryFile
+
+from vyos.config import Config
+from vyos.remote import urlc
+from vyos.component_version import system_footer
+from vyos.defaults import directories
+
+DEFAULT_CONFIG_PATH = os.path.join(directories['config'], 'config.boot')
+remote_save = None
+
+if len(sys.argv) > 1:
+ save_file = sys.argv[1]
+else:
+ save_file = DEFAULT_CONFIG_PATH
+
+if re.match(r'\w+:/', save_file):
+ try:
+ remote_save = urlc(save_file)
+ except ValueError as e:
+ sys.exit(e)
+
+config = Config()
+ct = config.get_config_tree(effective=True)
+
+write_file = save_file if remote_save is None else NamedTemporaryFile(delete=False).name
+with open(write_file, 'w') as f:
+ f.write(ct.to_string())
+ f.write("\n")
+ f.write(system_footer())
+
+if remote_save is not None:
+ try:
+ remote_save.upload(write_file)
+ finally:
+ os.remove(write_file)
diff --git a/src/helpers/vyos-sudo.py b/src/helpers/vyos-sudo.py
index 3e4c196d9..75dd7f29d 100755
--- a/src/helpers/vyos-sudo.py
+++ b/src/helpers/vyos-sudo.py
@@ -18,7 +18,7 @@
import os
import sys
-from vyos.util import is_admin
+from vyos.utils.permission import is_admin
if __name__ == '__main__':
diff --git a/src/helpers/vyos_config_sync.py b/src/helpers/vyos_config_sync.py
new file mode 100755
index 000000000..7cfa8fe88
--- /dev/null
+++ b/src/helpers/vyos_config_sync.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import os
+import json
+import requests
+import urllib3
+import logging
+from typing import Optional, List, Union, Dict, Any
+
+from vyos.config import Config
+from vyos.template import bracketize_ipv6
+
+
+CONFIG_FILE = '/run/config_sync_conf.conf'
+
+# Logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+logger.name = os.path.basename(__file__)
+
+# API
+API_HEADERS = {'Content-Type': 'application/json'}
+
+
+def post_request(url: str,
+ data: str,
+ headers: Dict[str, str]) -> requests.Response:
+ """Sends a POST request to the specified URL
+
+ Args:
+ url (str): The URL to send the POST request to.
+ data (Dict[str, Any]): The data to send with the POST request.
+ headers (Dict[str, str]): The headers to include with the POST request.
+
+ Returns:
+ requests.Response: The response object representing the server's response to the request
+ """
+
+ response = requests.post(url,
+ data=data,
+ headers=headers,
+ verify=False,
+ timeout=timeout)
+ return response
+
+
+def retrieve_config(section: str = None) -> Optional[Dict[str, Any]]:
+ """Retrieves the configuration from the local server.
+
+ Args:
+ section: str: The section of the configuration to retrieve. Default is None.
+
+ Returns:
+ Optional[Dict[str, Any]]: The retrieved configuration as a dictionary, or None if an error occurred.
+ """
+ if section is None:
+ section = []
+ else:
+ section = section.split()
+
+ conf = Config()
+ config = conf.get_config_dict(section, get_first_key=True)
+ if config:
+ return config
+ return None
+
+
+def set_remote_config(
+ address: str,
+ key: str,
+ op: str,
+ path: str = None,
+ section: Optional[str] = None) -> Optional[Dict[str, Any]]:
+ """Loads the VyOS configuration in JSON format to a remote host.
+
+ Args:
+ address (str): The address of the remote host.
+ key (str): The key to use for loading the configuration.
+ path (Optional[str]): The path of the configuration. Default is None.
+ section (Optional[str]): The section of the configuration to load. Default is None.
+
+ Returns:
+ Optional[Dict[str, Any]]: The response from the remote host as a dictionary, or None if an error occurred.
+ """
+
+ if path is None:
+ path = []
+ else:
+ path = path.split()
+ headers = {'Content-Type': 'application/json'}
+
+ # Disable the InsecureRequestWarning
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+ url = f'https://{address}/configure-section'
+ data = json.dumps({
+ 'op': mode,
+ 'path': path,
+ 'section': section,
+ 'key': key
+ })
+
+ try:
+ config = post_request(url, data, headers)
+ return config.json()
+ except requests.exceptions.RequestException as e:
+ print(f"An error occurred: {e}")
+ logger.error(f"An error occurred: {e}")
+ return None
+
+
+def is_section_revised(section: str) -> bool:
+ from vyos.config_mgmt import is_node_revised
+ return is_node_revised([section])
+
+
+def config_sync(secondary_address: str,
+ secondary_key: str,
+ sections: List[str],
+ mode: str):
+ """Retrieve a config section from primary router in JSON format and send it to
+ secondary router
+ """
+ # Config sync only if sections changed
+ if not any(map(is_section_revised, sections)):
+ return
+
+ logger.info(
+ f"Config synchronization: Mode={mode}, Secondary={secondary_address}"
+ )
+
+ # Sync sections ("nat", "firewall", etc)
+ for section in sections:
+ config_json = retrieve_config(section=section)
+ # Check if config path deesn't exist, for example "set nat"
+ # we set empty value for config_json data
+ # As we cannot send to the remote host section "nat None" config
+ if not config_json:
+ config_json = ""
+ logger.debug(
+ f"Retrieved config for section '{section}': {config_json}")
+ set_config = set_remote_config(address=secondary_address,
+ key=secondary_key,
+ op=mode,
+ path=section,
+ section=config_json)
+ logger.debug(f"Set config for section '{section}': {set_config}")
+
+
+if __name__ == '__main__':
+ # Read configuration from file
+ if not os.path.exists(CONFIG_FILE):
+ logger.error(f"Post-commit: No config file '{CONFIG_FILE}' exists")
+ exit(0)
+
+ with open(CONFIG_FILE, 'r') as f:
+ config_data = f.read()
+
+ config = json.loads(config_data)
+
+ mode = config.get('mode')
+ secondary_address = config.get('secondary', {}).get('address')
+ secondary_address = bracketize_ipv6(secondary_address)
+ secondary_key = config.get('secondary', {}).get('key')
+ sections = config.get('section')
+ timeout = int(config.get('secondary', {}).get('timeout'))
+
+ if not all([
+ mode, secondary_address, secondary_key, sections
+ ]):
+ logger.error(
+ "Missing required configuration data for config synchronization.")
+ exit(0)
+
+ config_sync(secondary_address, secondary_key,
+ sections, mode)
diff --git a/src/helpers/vyos_net_name b/src/helpers/vyos_net_name
index 1798e92db..8c0992414 100755
--- a/src/helpers/vyos_net_name
+++ b/src/helpers/vyos_net_name
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import os
import re
@@ -26,7 +24,8 @@ from sys import argv
from vyos.configtree import ConfigTree
from vyos.defaults import directories
-from vyos.util import cmd, boot_configuration_complete
+from vyos.utils.process import cmd
+from vyos.utils.boot import boot_configuration_complete
from vyos.migrator import VirtualMigrator
vyos_udev_dir = directories['vyos_udev_dir']
diff --git a/src/init/vyos-config b/src/init/vyos-config
new file mode 100755
index 000000000..356427024
--- /dev/null
+++ b/src/init/vyos-config
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+while [ ! -f /tmp/vyos-config-status ]
+do
+ sleep 1
+done
+
+status=$(cat /tmp/vyos-config-status)
+
+if [ -z "$1" ]; then
+ if [ $status -ne 0 ]; then
+ echo "Configuration error"
+ else
+ echo "Configuration success"
+ fi
+fi
diff --git a/src/init/vyos-router b/src/init/vyos-router
new file mode 100755
index 000000000..96f163213
--- /dev/null
+++ b/src/init/vyos-router
@@ -0,0 +1,445 @@
+#!/bin/bash
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. /lib/lsb/init-functions
+
+: ${vyatta_env:=/etc/default/vyatta}
+source $vyatta_env
+
+declare progname=${0##*/}
+declare action=$1; shift
+
+declare -x BOOTFILE=$vyatta_sysconfdir/config/config.boot
+
+# If vyos-config= boot option is present, use that file instead
+for x in $(cat /proc/cmdline); do
+ [[ $x = vyos-config=* ]] || continue
+ VYOS_CONFIG="${x#vyos-config=}"
+done
+
+if [ ! -z "$VYOS_CONFIG" ]; then
+ if [ -r "$VYOS_CONFIG" ]; then
+ echo "Config selected manually: $VYOS_CONFIG"
+ declare -x BOOTFILE="$VYOS_CONFIG"
+ else
+ echo "WARNING: Could not read selected config file, using default!"
+ fi
+fi
+
+declare -a subinit
+declare -a all_subinits=( firewall )
+
+if [ $# -gt 0 ] ; then
+ for s in $@ ; do
+ [ -x ${vyatta_sbindir}/${s}.init ] && subinit[${#subinit}]=$s
+ done
+else
+ for s in ${all_subinits[@]} ; do
+ [ -x ${vyatta_sbindir}/${s}.init ] && subinit[${#subinit}]=$s
+ done
+fi
+
+GROUP=vyattacfg
+
+# easy way to make empty file without any command
+empty()
+{
+ >$1
+}
+
+# check if bootup of this portion is disabled
+disabled () {
+ grep -q -w no-vyos-$1 /proc/cmdline
+}
+
+# if necessary, provide initial config
+init_bootfile () {
+ if [ ! -r $BOOTFILE ] ; then
+ if [ -f $vyatta_sysconfdir/config.boot.default ]; then
+ cp $vyatta_sysconfdir/config.boot.default $BOOTFILE
+ else
+ $vyos_libexec_dir/system-versions-foot.py > $BOOTFILE
+ fi
+ chgrp ${GROUP} $BOOTFILE
+ chmod 660 $BOOTFILE
+ fi
+}
+
+# if necessary, migrate initial config
+migrate_bootfile ()
+{
+ if [ -x $vyos_libexec_dir/run-config-migration.py ]; then
+ log_progress_msg migrate
+ sg ${GROUP} -c "$vyos_libexec_dir/run-config-migration.py $BOOTFILE"
+ fi
+}
+
+# load the initial config
+load_bootfile ()
+{
+ log_progress_msg configure
+ (
+ if [ -f /etc/default/vyatta-load-boot ]; then
+ # build-specific environment for boot-time config loading
+ source /etc/default/vyatta-load-boot
+ fi
+ if [ -x $vyos_libexec_dir/vyos-boot-config-loader.py ]; then
+ sg ${GROUP} -c "$vyos_libexec_dir/vyos-boot-config-loader.py $BOOTFILE"
+ fi
+ )
+}
+
+# restore if missing pre-config script
+restore_if_missing_preconfig_script ()
+{
+ if [ ! -x ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script ]; then
+ cp ${vyos_rootfs_dir}/opt/vyatta/etc/config/scripts/vyos-preconfig-bootup.script ${vyatta_sysconfdir}/config/scripts/
+ chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script
+ chmod 750 ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script
+ fi
+}
+
+# execute the pre-config script
+run_preconfig_script ()
+{
+ if [ -x $vyatta_sysconfdir/config/scripts/vyos-preconfig-bootup.script ]; then
+ $vyatta_sysconfdir/config/scripts/vyos-preconfig-bootup.script
+ fi
+}
+
+# restore if missing post-config script
+restore_if_missing_postconfig_script ()
+{
+ if [ ! -x ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script ]; then
+ cp ${vyos_rootfs_dir}/opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script ${vyatta_sysconfdir}/config/scripts/
+ chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script
+ chmod 750 ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script
+ fi
+}
+
+# execute the post-config scripts
+run_postconfig_scripts ()
+{
+ if [ -x $vyatta_sysconfdir/config/scripts/vyatta-postconfig-bootup.script ]; then
+ $vyatta_sysconfdir/config/scripts/vyatta-postconfig-bootup.script
+ fi
+ if [ -x $vyatta_sysconfdir/config/scripts/vyos-postconfig-bootup.script ]; then
+ $vyatta_sysconfdir/config/scripts/vyos-postconfig-bootup.script
+ fi
+}
+
+run_postupgrade_script ()
+{
+ if [ -f $vyatta_sysconfdir/config/.upgraded ]; then
+ # Run the system script
+ /usr/libexec/vyos/system/post-upgrade
+
+ # Run user scripts
+ if [ -d $vyatta_sysconfdir/config/scripts/post-upgrade.d ]; then
+ run-parts $vyatta_sysconfdir/config/scripts/post-upgrade.d
+ fi
+ rm -f $vyatta_sysconfdir/config/.upgraded
+ fi
+}
+
+#
+# On image booted machines, we need to mount /boot from the image-specific
+# boot directory so that kernel package installation will put the
+# files in the right place. We also have to mount /boot/grub from the
+# system-wide grub directory so that tools that edit the grub.cfg
+# file will find it in the expected location.
+#
+bind_mount_boot ()
+{
+ persist_path=$(/opt/vyatta/sbin/vyos-persistpath)
+ if [ $? == 0 ]; then
+ if [ -e $persist_path/boot ]; then
+ image_name=$(cat /proc/cmdline | sed -e s+^.*vyos-union=/boot/++ | sed -e 's/ .*$//')
+
+ if [ -n "$image_name" ]; then
+ mount --bind $persist_path/boot/$image_name /boot
+ if [ $? -ne 0 ]; then
+ echo "Couldn't bind mount /boot"
+ fi
+
+ if [ ! -d /boot/grub ]; then
+ mkdir /boot/grub
+ fi
+
+ mount --bind $persist_path/boot/grub /boot/grub
+ if [ $? -ne 0 ]; then
+ echo "Couldn't bind mount /boot/grub"
+ fi
+ fi
+ fi
+ fi
+}
+
+clear_or_override_config_files ()
+{
+ for conf in snmp/snmpd.conf snmp/snmptrapd.conf snmp/snmp.conf \
+ keepalived/keepalived.conf cron.d/vyos-crontab \
+ ipvsadm.rules default/ipvsadm resolv.conf
+ do
+ if [ -s /etc/$conf ] ; then
+ empty /etc/$conf
+ chmod 0644 /etc/$conf
+ fi
+ done
+}
+
+update_interface_config ()
+{
+ if [ -d /run/udev/vyos ]; then
+ $vyos_libexec_dir/vyos-interface-rescan.py $BOOTFILE
+ fi
+}
+
+cleanup_post_commit_hooks () {
+ # Remove links from the post-commit hooks directory.
+ # note that this approach only supports hooks that are "configured",
+ # i.e., it does not support hooks that need to always be present.
+ cpostdir=$(cli-shell-api getPostCommitHookDir)
+ # exclude commits hooks from vyatta-cfg
+ excluded="10vyatta-log-commit.pl 99vyos-user-postcommit-hooks"
+ if [ -d "$cpostdir" ]; then
+ for f in $cpostdir/*; do
+ if [[ ! $excluded =~ $(basename $f) ]]; then
+ rm -f $cpostdir/$(basename $f)
+ fi
+ done
+ fi
+}
+
+# These are all the default security setting which are later
+# overridden when configuration is read. These are the values the
+# system defaults.
+security_reset ()
+{
+ # restore PAM back to virgin state (no radius/tacacs services)
+ pam-auth-update --package --remove radius
+ rm -f /etc/pam_radius_auth.conf
+ pam-auth-update --package --remove tacplus
+ rm -f /etc/tacplus_nss.conf /etc/tacplus_servers
+
+ # Certain configuration files are re-generated by the configuration
+ # subsystem and must reside under /etc and can not easily be moved to /run.
+ # So on every boot we simply delete any remaining files and let the CLI
+ # regenearte them.
+
+ # PPPoE
+ rm -f /etc/ppp/peers/pppoe* /etc/ppp/peers/wlm*
+
+ # IPSec
+ rm -rf /etc/ipsec.conf /etc/ipsec.secrets
+ find /etc/swanctl -type f | xargs rm -f
+
+ # limit cleanup
+ rm -f /etc/security/limits.d/10-vyos.conf
+
+ # iproute2 cleanup
+ rm -f /etc/iproute2/rt_tables.d/vyos-*.conf
+
+ # Container
+ rm -f /etc/containers/storage.conf /etc/containers/registries.conf /etc/containers/containers.conf
+ # Clean all networks and re-create them from our CLI
+ rm -f /etc/containers/networks/*
+
+ # System Options (SSH/cURL)
+ rm -f /etc/ssh/ssh_config.d/*vyos*.conf
+ rm -f /etc/curlrc
+}
+
+# XXX: T3885 - generate persistend DHCPv6 DUID (Type4 - UUID based)
+gen_duid ()
+{
+ DUID_FILE="/var/lib/dhcpv6/dhcp6c_duid"
+ UUID_FILE="/sys/class/dmi/id/product_uuid"
+ UUID_FILE_ALT="/sys/class/dmi/id/product_serial"
+ if [ ! -f ${UUID_FILE} ] && [ ! -f ${UUID_FILE_ALT} ]; then
+ return 1
+ fi
+
+ # DUID is based on the BIOS/EFI UUID. We omit additional - characters
+ if [ -f ${UUID_FILE} ]; then
+ UUID=$(cat ${UUID_FILE} | tr -d -)
+ fi
+ if [ -z ${UUID} ]; then
+ UUID=$(uuidgen --sha1 --namespace @dns --name $(cat ${UUID_FILE_ALT}) | tr -d -)
+ fi
+ # Add DUID type4 (UUID) information
+ DUID_TYPE="0004"
+
+ # The length-information (as per RFC6355 UUID is 128 bits long) is in big-endian
+ # format - beware when porting to ARM64. The length field consists out of the
+ # UUID (128 bit + 16 bits DUID type) resulting in hex 12.
+ DUID_LEN="0012"
+ if [ "$(echo -n I | od -to2 | head -n1 | cut -f2 -d" " | cut -c6 )" -eq 1 ]; then
+ # true on little-endian (x86) systems
+ DUID_LEN="1200"
+ fi
+
+ for i in $(echo -n ${DUID_LEN}${DUID_TYPE}${UUID} | sed 's/../& /g'); do
+ echo -ne "\x$i"
+ done > ${DUID_FILE}
+}
+
+start ()
+{
+ # reset and clean config files
+ security_reset || log_failure_msg "security reset failed"
+
+ # some legacy directories migrated over from old rl-system.init
+ mkdir -p /var/run/vyatta /var/log/vyatta
+ chgrp vyattacfg /var/run/vyatta /var/log/vyatta
+ chmod 775 /var/run/vyatta /var/log/vyatta
+
+ log_daemon_msg "Waiting for NICs to settle down"
+ # On boot time udev migth take a long time to reorder nic's, this will ensure that
+ # all udev activity is completed and all nics presented at boot-time will have their
+ # final name before continuing with vyos-router initialization.
+ SECONDS=0
+ udevadm settle
+ STATUS=$?
+ log_progress_msg "settled in ${SECONDS}sec."
+ log_end_msg ${STATUS}
+
+ # mountpoint for bpf maps required by xdp
+ mount -t bpf none /sys/fs/bpf
+
+ # Clear out Debian APT source config file
+ empty /etc/apt/sources.list
+
+ # Generate DHCPv6 DUID
+ gen_duid || log_failure_msg "could not generate DUID"
+
+ # Mount a temporary filesystem for container networks.
+ # Configuration should be loaded from VyOS cli.
+ cni_dir="/etc/cni/net.d"
+ [ ! -d ${cni_dir} ] && mkdir -p ${cni_dir}
+ mount -t tmpfs none ${cni_dir}
+
+ # Init firewall
+ nfct helper add rpc inet tcp
+ nfct helper add rpc inet udp
+ nfct helper add tns inet tcp
+ nft -f /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules"
+
+ rm -f /etc/hostname
+ ${vyos_conf_scripts_dir}/host_name.py || log_failure_msg "could not reset host-name"
+ systemctl start frr.service
+
+ # As VyOS does not execute commands that are not present in the CLI we call
+ # the script by hand to have a single source for the login banner and MOTD
+ ${vyos_conf_scripts_dir}/system_console.py || log_failure_msg "could not reset serial console"
+ ${vyos_conf_scripts_dir}/system-login.py || log_failure_msg "could not reset system login"
+ ${vyos_conf_scripts_dir}/system-login-banner.py || log_failure_msg "could not reset motd and issue files"
+ ${vyos_conf_scripts_dir}/system-option.py || log_failure_msg "could not reset system option files"
+ ${vyos_conf_scripts_dir}/conntrack.py || log_failure_msg "could not reset conntrack subsystem"
+ ${vyos_conf_scripts_dir}/container.py || log_failure_msg "could not reset container subsystem"
+
+ clear_or_override_config_files || log_failure_msg "could not reset config files"
+
+ # enable some debugging before loading the configuration
+ if grep -q vyos-debug /proc/cmdline; then
+ log_action_begin_msg "Enable runtime debugging options"
+ touch /tmp/vyos.container.debug
+ touch /tmp/vyos.ifconfig.debug
+ touch /tmp/vyos.frr.debug
+ touch /tmp/vyos.container.debug
+ fi
+
+ log_action_begin_msg "Mounting VyOS Config"
+ # ensure the vyatta_configdir supports a large number of inodes since
+ # the config hierarchy is often inode-bound (instead of size).
+ # impose a minimum and then scale up dynamically with the actual size
+ # of the system memory.
+ local tmem=$(sed -n 's/^MemTotal: \+\([0-9]\+\) kB$/\1/p' /proc/meminfo)
+ local tpages
+ local tmpfs_opts="nosuid,nodev,mode=775,nr_inodes=0" #automatically allocate inodes
+ mount -o $tmpfs_opts -t tmpfs none ${vyatta_configdir} \
+ && chgrp ${GROUP} ${vyatta_configdir}
+ log_action_end_msg $?
+
+ disabled bootfile || init_bootfile
+
+ cleanup_post_commit_hooks
+
+ log_daemon_msg "Starting VyOS router"
+ disabled migrate || migrate_bootfile
+
+ restore_if_missing_preconfig_script
+
+ run_preconfig_script
+
+ run_postupgrade_script
+
+ update_interface_config
+
+ for s in ${subinit[@]} ; do
+ if ! disabled $s; then
+ log_progress_msg $s
+ if ! ${vyatta_sbindir}/${s}.init start
+ then log_failure_msg
+ exit 1
+ fi
+ fi
+ done
+
+ bind_mount_boot
+
+ disabled configure || load_bootfile
+ log_end_msg $?
+
+ telinit q
+ chmod g-w,o-w /
+
+ restore_if_missing_postconfig_script
+
+ run_postconfig_scripts
+}
+
+stop()
+{
+ local -i status=0
+ log_daemon_msg "Stopping VyOS router"
+ for ((i=${#sub_inits[@]} - 1; i >= 0; i--)) ; do
+ s=${subinit[$i]}
+ log_progress_msg $s
+ ${vyatta_sbindir}/${s}.init stop
+ let status\|=$?
+ done
+ log_end_msg $status
+ log_action_begin_msg "Un-mounting VyOS Config"
+ umount ${vyatta_configdir}
+ log_action_end_msg $?
+
+ systemctl stop frr.service
+}
+
+case "$action" in
+ start) start ;;
+ stop) stop ;;
+ restart|force-reload) stop && start ;;
+ *) log_failure_msg "usage: $progname [ start|stop|restart ] [ subinit ... ]" ;
+ false ;;
+esac
+
+exit $?
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 4
+# End:
diff --git a/src/migration-scripts/bgp/0-to-1 b/src/migration-scripts/bgp/0-to-1
index 5e9dffe1f..03c45107b 100755
--- a/src/migration-scripts/bgp/0-to-1
+++ b/src/migration-scripts/bgp/0-to-1
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/bgp/1-to-2 b/src/migration-scripts/bgp/1-to-2
index e2d3fcd33..96b939b47 100755
--- a/src/migration-scripts/bgp/1-to-2
+++ b/src/migration-scripts/bgp/1-to-2
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/bgp/2-to-3 b/src/migration-scripts/bgp/2-to-3
index 7ced0a3b0..34d321a96 100755
--- a/src/migration-scripts/bgp/2-to-3
+++ b/src/migration-scripts/bgp/2-to-3
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/bgp/3-to-4 b/src/migration-scripts/bgp/3-to-4
index 0df2fbec4..894cdda2b 100755
--- a/src/migration-scripts/bgp/3-to-4
+++ b/src/migration-scripts/bgp/3-to-4
@@ -22,7 +22,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/config-management/0-to-1 b/src/migration-scripts/config-management/0-to-1
index 344359110..6528fd136 100755
--- a/src/migration-scripts/config-management/0-to-1
+++ b/src/migration-scripts/config-management/0-to-1
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/conntrack-sync/1-to-2 b/src/migration-scripts/conntrack-sync/1-to-2
index ebbd8c35a..a8e1007f3 100755
--- a/src/migration-scripts/conntrack-sync/1-to-2
+++ b/src/migration-scripts/conntrack-sync/1-to-2
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/conntrack/1-to-2 b/src/migration-scripts/conntrack/1-to-2
index 4fc88a1ed..c4fe667fc 100755
--- a/src/migration-scripts/conntrack/1-to-2
+++ b/src/migration-scripts/conntrack/1-to-2
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/conntrack/2-to-3 b/src/migration-scripts/conntrack/2-to-3
index 8a8b43279..6fb457b7f 100755
--- a/src/migration-scripts/conntrack/2-to-3
+++ b/src/migration-scripts/conntrack/2-to-3
@@ -8,7 +8,7 @@ import sys
from vyos.configtree import ConfigTree
from vyos.version import get_version
-if len(sys.argv) < 1:
+if len(sys.argv) < 2:
print('Must specify file name!')
sys.exit(1)
diff --git a/src/migration-scripts/container/0-to-1 b/src/migration-scripts/container/0-to-1
index d0461389b..6b282e082 100755
--- a/src/migration-scripts/container/0-to-1
+++ b/src/migration-scripts/container/0-to-1
@@ -21,9 +21,9 @@ import shutil
import sys
from vyos.configtree import ConfigTree
-from vyos.util import call
+from vyos.utils.process import call
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
@@ -39,12 +39,12 @@ config = ConfigTree(config_file)
if config.exists(base):
for container in config.list_nodes(base):
# Stop any given container first
- call(f'systemctl stop vyos-container-{container}.service')
+ call(f'sudo systemctl stop vyos-container-{container}.service')
# Export container image for later re-import to new filesystem. We store
# the backup on a real disk as a tmpfs (like /tmp) could probably lack
# memory if a host has too many containers stored.
image_name = config.return_value(base + [container, 'image'])
- call(f'podman image save --quiet --output /root/{container}.tar --format oci-archive {image_name}')
+ call(f'sudo podman image save --quiet --output /root/{container}.tar --format oci-archive {image_name}')
# No need to adjust the strage driver online (this is only used for testing and
# debugging on a live system) - it is already overlay2 when the migration script
@@ -66,10 +66,10 @@ if config.exists(base):
# Export container image for later re-import to new filesystem
image_name = config.return_value(base + [container, 'image'])
image_path = f'/root/{container}.tar'
- call(f'podman image load --quiet --input {image_path}')
+ call(f'sudo podman image load --quiet --input {image_path}')
# Start any given container first
- call(f'systemctl start vyos-container-{container}.service')
+ call(f'sudo systemctl start vyos-container-{container}.service')
# Delete temporary container image
if os.path.exists(image_path):
diff --git a/src/migration-scripts/dhcp-relay/1-to-2 b/src/migration-scripts/dhcp-relay/1-to-2
index b72da1028..508bac6be 100755
--- a/src/migration-scripts/dhcp-relay/1-to-2
+++ b/src/migration-scripts/dhcp-relay/1-to-2
@@ -7,7 +7,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/dhcp-server/4-to-5 b/src/migration-scripts/dhcp-server/4-to-5
index 313b5279a..d15e0baf5 100755
--- a/src/migration-scripts/dhcp-server/4-to-5
+++ b/src/migration-scripts/dhcp-server/4-to-5
@@ -9,7 +9,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6
index aefe84737..f5c766a09 100755
--- a/src/migration-scripts/dhcp-server/5-to-6
+++ b/src/migration-scripts/dhcp-server/5-to-6
@@ -20,7 +20,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/dhcpv6-server/0-to-1 b/src/migration-scripts/dhcpv6-server/0-to-1
index 6f1150da1..deae1ca29 100755
--- a/src/migration-scripts/dhcpv6-server/0-to-1
+++ b/src/migration-scripts/dhcpv6-server/0-to-1
@@ -19,7 +19,7 @@
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/dns-dynamic/0-to-1 b/src/migration-scripts/dns-dynamic/0-to-1
new file mode 100755
index 000000000..d80e8d44a
--- /dev/null
+++ b/src/migration-scripts/dns-dynamic/0-to-1
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2023 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/>.
+
+# T5144:
+# - migrate "service dns dynamic interface ..."
+# to "service dns dynamic address ..."
+# - migrate "service dns dynamic interface <interface> use-web ..."
+# to "service dns dynamic address <address> web-options ..."
+# - migrate "service dns dynamic interface <interface> rfc2136 <config> record ..."
+# to "service dns dynamic address <address> rfc2136 <config> host-name ..."
+# - migrate "service dns dynamic interface <interface> service <config> login ..."
+# to "service dns dynamic address <address> service <config> username ..."
+# - apply global 'ipv6-enable' to per <config> 'ip-version: ipv6'
+# - apply service protocol mapping upfront, they are not 'auto-detected' anymore
+
+import sys
+from vyos.configtree import ConfigTree
+
+service_protocol_mapping = {
+ 'afraid': 'freedns',
+ 'changeip': 'changeip',
+ 'cloudflare': 'cloudflare',
+ 'dnspark': 'dnspark',
+ 'dslreports': 'dslreports1',
+ 'dyndns': 'dyndns2',
+ 'easydns': 'easydns',
+ 'namecheap': 'namecheap',
+ 'noip': 'noip',
+ 'sitelutions': 'sitelutions',
+ 'zoneedit': 'zoneedit1'
+}
+
+if len(sys.argv) < 2:
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+old_base_path = ['service', 'dns', 'dynamic', 'interface']
+new_base_path = ['service', 'dns', 'dynamic', 'address']
+
+if not config.exists(old_base_path):
+ # Nothing to do
+ sys.exit(0)
+
+# Migrate "service dns dynamic interface"
+# to "service dns dynamic address"
+config.rename(old_base_path, new_base_path[-1])
+
+for address in config.list_nodes(new_base_path):
+ # Migrate "service dns dynamic interface <interface> rfc2136 <config> record"
+ # to "service dns dynamic address <address> rfc2136 <config> host-name"
+ if config.exists(new_base_path + [address, 'rfc2136']):
+ for rfc_cfg in config.list_nodes(new_base_path + [address, 'rfc2136']):
+ if config.exists(new_base_path + [address, 'rfc2136', rfc_cfg, 'record']):
+ config.rename(new_base_path + [address, 'rfc2136', rfc_cfg, 'record'], 'host-name')
+
+ # Migrate "service dns dynamic interface <interface> service <config> login"
+ # to "service dns dynamic address <address> service <config> username"
+ if config.exists(new_base_path + [address, 'service']):
+ for svc_cfg in config.list_nodes(new_base_path + [address, 'service']):
+ if config.exists(new_base_path + [address, 'service', svc_cfg, 'login']):
+ config.rename(new_base_path + [address, 'service', svc_cfg, 'login'], 'username')
+ # Apply global 'ipv6-enable' to per <config> 'ip-version: ipv6'
+ if config.exists(new_base_path + [address, 'ipv6-enable']):
+ config.set(new_base_path + [address, 'service', svc_cfg, 'ip-version'],
+ value='ipv6', replace=False)
+ config.delete(new_base_path + [address, 'ipv6-enable'])
+ # Apply service protocol mapping upfront, they are not 'auto-detected' anymore
+ if svc_cfg in service_protocol_mapping:
+ config.set(new_base_path + [address, 'service', svc_cfg, 'protocol'],
+ value=service_protocol_mapping.get(svc_cfg), replace=False)
+
+ # Migrate "service dns dynamic interface <interface> use-web"
+ # to "service dns dynamic address <address> web-options"
+ # Also, rename <address> to 'web' literal for backward compatibility
+ if config.exists(new_base_path + [address, 'use-web']):
+ config.rename(new_base_path + [address], 'web')
+ config.rename(new_base_path + ['web', 'use-web'], 'web-options')
+
+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))
+ sys.exit(1)
diff --git a/src/migration-scripts/dns-forwarding/0-to-1 b/src/migration-scripts/dns-forwarding/0-to-1
index 6e8720eef..7f4343652 100755
--- a/src/migration-scripts/dns-forwarding/0-to-1
+++ b/src/migration-scripts/dns-forwarding/0-to-1
@@ -22,7 +22,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2
index a8c930be7..7df2d47e2 100755
--- a/src/migration-scripts/dns-forwarding/1-to-2
+++ b/src/migration-scripts/dns-forwarding/1-to-2
@@ -25,7 +25,7 @@ from sys import argv, exit
from vyos.ifconfig import Interface
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/dns-forwarding/2-to-3 b/src/migration-scripts/dns-forwarding/2-to-3
index 01e445b22..d7ff9e260 100755
--- a/src/migration-scripts/dns-forwarding/2-to-3
+++ b/src/migration-scripts/dns-forwarding/2-to-3
@@ -21,7 +21,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/dns-forwarding/3-to-4 b/src/migration-scripts/dns-forwarding/3-to-4
index 55165c2c5..3d5316ed4 100755
--- a/src/migration-scripts/dns-forwarding/3-to-4
+++ b/src/migration-scripts/dns-forwarding/3-to-4
@@ -20,7 +20,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11
new file mode 100755
index 000000000..716c5a240
--- /dev/null
+++ b/src/migration-scripts/firewall/10-to-11
@@ -0,0 +1,374 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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/>.
+
+# T5160: Firewall re-writing
+
+# cli changes from:
+# set firewall name <name> ...
+# set firewall ipv6-name <name> ...
+# To
+# set firewall ipv4 name <name>
+# set firewall ipv6 name <name>
+
+## Also from 'firewall interface' removed.
+## in and out:
+ # set firewall interface <iface> [in|out] [name | ipv6-name] <name>
+ # To
+ # set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> [inbound-interface | outboubd-interface] interface-name <iface>
+ # set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> action jump
+ # set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> jump-target <name>
+## local:
+ # set firewall interface <iface> local [name | ipv6-name] <name>
+ # To
+ # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> inbound-interface interface-name <iface>
+ # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> action jump
+ # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> jump-target <name>
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+from vyos.ifconfig import Section
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['firewall']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+### Migration of state policies
+if config.exists(base + ['state-policy']):
+ for family in ['ipv4', 'ipv6']:
+ for hook in ['forward', 'input', 'output']:
+ for priority in ['filter']:
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + [family, hook, priority, 'default-action'], value='accept')
+ position = 1
+ for state in config.list_nodes(base + ['state-policy']):
+ action = config.return_value(base + ['state-policy', state, 'action'])
+ config.set(base + [family, hook, priority, 'rule'])
+ config.set_tag(base + [family, hook, priority, 'rule'])
+ config.set(base + [family, hook, priority, 'rule', position, 'state', state], value='enable')
+ config.set(base + [family, hook, priority, 'rule', position, 'action'], value=action)
+ position = position + 1
+ config.delete(base + ['state-policy'])
+
+## migration of global options:
+for option in ['all-ping', 'broadcast-ping', 'config-trap', 'ip-src-route', 'ipv6-receive-redirects', 'ipv6-src-route', 'log-martians',
+ 'receive-redirects', 'resolver-cache', 'resolver-internal', 'send-redirects', 'source-validation', 'syn-cookies', 'twa-hazards-protection']:
+ if config.exists(base + [option]):
+ if option != 'config-trap':
+ val = config.return_value(base + [option])
+ config.set(base + ['global-options', option], value=val)
+ config.delete(base + [option])
+
+### Migration of firewall name and ipv6-name
+if config.exists(base + ['name']):
+ config.set(['firewall', 'ipv4', 'name'])
+ config.set_tag(['firewall', 'ipv4', 'name'])
+
+ for ipv4name in config.list_nodes(base + ['name']):
+ config.copy(base + ['name', ipv4name], base + ['ipv4', 'name', ipv4name])
+ config.delete(base + ['name'])
+
+if config.exists(base + ['ipv6-name']):
+ config.set(['firewall', 'ipv6', 'name'])
+ config.set_tag(['firewall', 'ipv6', 'name'])
+
+ for ipv6name in config.list_nodes(base + ['ipv6-name']):
+ config.copy(base + ['ipv6-name', ipv6name], base + ['ipv6', 'name', ipv6name])
+ config.delete(base + ['ipv6-name'])
+
+### Migration of firewall interface
+if config.exists(base + ['interface']):
+ fwd_ipv4_rule = 5
+ inp_ipv4_rule = 5
+ fwd_ipv6_rule = 5
+ inp_ipv6_rule = 5
+ for iface in config.list_nodes(base + ['interface']):
+ for direction in ['in', 'out', 'local']:
+ if config.exists(base + ['interface', iface, direction]):
+ if config.exists(base + ['interface', iface, direction, 'name']):
+ target = config.return_value(base + ['interface', iface, direction, 'name'])
+ if direction == 'in':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv4', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ elif direction == 'out':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv4', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv4_rule, 'outbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ else:
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv4', 'input', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [inp_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [inp_ipv4_rule, 'action'], value='jump')
+ config.set(new_base + [inp_ipv4_rule, 'jump-target'], value=target)
+ inp_ipv4_rule = inp_ipv4_rule + 5
+
+ if config.exists(base + ['interface', iface, direction, 'ipv6-name']):
+ target = config.return_value(base + ['interface', iface, direction, 'ipv6-name'])
+ if direction == 'in':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv6', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+ elif direction == 'out':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv6', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv6_rule, 'outbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+ else:
+ new_base = base + ['ipv6', 'input', 'filter', 'rule']
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [inp_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [inp_ipv6_rule, 'action'], value='jump')
+ config.set(new_base + [inp_ipv6_rule, 'jump-target'], value=target)
+ inp_ipv6_rule = inp_ipv6_rule + 5
+
+ config.delete(base + ['interface'])
+
+
+### Migration of zones:
+### User interface groups
+if config.exists(base + ['zone']):
+ inp_ipv4_rule = 101
+ inp_ipv6_rule = 101
+ fwd_ipv4_rule = 101
+ fwd_ipv6_rule = 101
+ out_ipv4_rule = 101
+ out_ipv6_rule = 101
+ local_zone = 'False'
+
+ for zone in config.list_nodes(base + ['zone']):
+ if config.exists(base + ['zone', zone, 'local-zone']):
+ local_zone = 'True'
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept')
+ config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
+ config.set(base + ['ipv4', 'output', 'filter', 'default-action'], value='accept')
+ config.set(base + ['ipv6', 'output', 'filter', 'default-action'], value='accept')
+ for from_zone in config.list_nodes(base + ['zone', zone, 'from']):
+ group_name = 'IG_' + from_zone
+ if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']):
+ # ipv4 input ruleset
+ target_ipv4_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'name'])
+ config.set(base + ['ipv4', 'input', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'input', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'inbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'action'], value='jump')
+ config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'jump-target'], value=target_ipv4_chain)
+ inp_ipv4_rule = inp_ipv4_rule + 5
+ if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']):
+ # ipv6 input ruleset
+ target_ipv6_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name'])
+ config.set(base + ['ipv6', 'input', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'input', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'inbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'action'], value='jump')
+ config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'jump-target'], value=target_ipv6_chain)
+ inp_ipv6_rule = inp_ipv6_rule + 5
+
+ # Migrate: set firewall zone <zone> default-action <action>
+ # Options: drop or reject. If not specified, is drop
+ if config.exists(base + ['zone', zone, 'default-action']):
+ local_def_action = config.return_value(base + ['zone', zone, 'default-action'])
+ else:
+ local_def_action = 'drop'
+ config.set(base + ['ipv4', 'input', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'input', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'action'], value=local_def_action)
+ config.set(base + ['ipv6', 'input', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'input', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'action'], value=local_def_action)
+ if config.exists(base + ['zone', zone, 'enable-default-log']):
+ config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'log'], value='enable')
+ config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'log'], value='enable')
+
+ else:
+ # It's not a local zone
+ group_name = 'IG_' + zone
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
+ config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+ # intra-filtering migration. By default accept
+ intra_zone_ipv4_action = 'accept'
+ intra_zone_ipv6_action = 'accept'
+
+ if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'action']):
+ intra_zone_ipv4_action = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'action'])
+ intra_zone_ipv6_action = intra_zone_ipv4_action
+ else:
+ if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']):
+ intra_zone_ipv4_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name'])
+ intra_zone_ipv4_action = 'jump'
+ if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']):
+ intra_zone_ipv6_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name'])
+ intra_zone_ipv6_action = 'jump'
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'forward', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'inbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value=intra_zone_ipv4_action)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'inbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value=intra_zone_ipv6_action)
+ if intra_zone_ipv4_action == 'jump':
+ if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']):
+ intra_zone_ipv4_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name'])
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'jump-target'], value=intra_zone_ipv4_target)
+ if intra_zone_ipv6_action == 'jump':
+ if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']):
+ intra_zone_ipv6_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name'])
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'jump-target'], value=intra_zone_ipv6_target)
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+
+ if config.exists(base + ['zone', zone, 'interface']):
+ # Create interface group IG_<zone>
+ group_name = 'IG_' + zone
+ config.set(base + ['group', 'interface-group'], value=group_name)
+ config.set_tag(base + ['group', 'interface-group'])
+ for iface in config.return_values(base + ['zone', zone, 'interface']):
+ config.set(base + ['group', 'interface-group', group_name, 'interface'], value=iface, replace=False)
+
+ if config.exists(base + ['zone', zone, 'from']):
+ for from_zone in config.list_nodes(base + ['zone', zone, 'from']):
+ from_group = 'IG_' + from_zone
+ if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']):
+ target_ipv4_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'name'])
+ if config.exists(base + ['zone', from_zone, 'local-zone']):
+ # It's from LOCAL zone -> Output filtering
+ config.set(base + ['ipv4', 'output', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'output', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'action'], value='jump')
+ config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'jump-target'], value=target_ipv4_chain)
+ out_ipv4_rule = out_ipv4_rule + 5
+ else:
+ # It's not LOCAL zone -> forward filtering
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'forward', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'inbound-interface', 'interface-group'], value=from_group)
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value='jump')
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'jump-target'], value=target_ipv4_chain)
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']):
+ target_ipv6_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name'])
+ if config.exists(base + ['zone', from_zone, 'local-zone']):
+ # It's from LOCAL zone -> Output filtering
+ config.set(base + ['ipv6', 'output', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'output', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'action'], value='jump')
+ config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'jump-target'], value=target_ipv6_chain)
+ out_ipv6_rule = out_ipv6_rule + 5
+ else:
+ # It's not LOCAL zone -> forward filtering
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'inbound-interface', 'interface-group'], value=from_group)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value='jump')
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'jump-target'], value=target_ipv6_chain)
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+
+ ## Now need to migrate: set firewall zone <zone> default-action <action> # action=drop if not specified.
+ if config.exists(base + ['zone', zone, 'default-action']):
+ def_action = config.return_value(base + ['zone', zone, 'default-action'])
+ else:
+ def_action = 'drop'
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'forward', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value=def_action)
+ description = 'zone_' + zone + ' default-action'
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'description'], value=description)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value=def_action)
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'description'], value=description)
+
+ if config.exists(base + ['zone', zone, 'enable-default-log']):
+ config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'log'], value='enable')
+ config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'log'], value='enable')
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+
+ # Migrate default-action (force to be drop in output chain) if local zone is defined
+ if local_zone == 'True':
+ # General drop in output change if needed
+ config.set(base + ['ipv4', 'output', 'filter', 'rule'])
+ config.set_tag(base + ['ipv4', 'output', 'filter', 'rule'])
+ config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'action'], value=local_def_action)
+ config.set(base + ['ipv6', 'output', 'filter', 'rule'])
+ config.set_tag(base + ['ipv6', 'output', 'filter', 'rule'])
+ config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'action'], value=local_def_action)
+
+ config.delete(base + ['zone'])
+
+###### END migration zones
+
+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) \ No newline at end of file
diff --git a/src/migration-scripts/firewall/5-to-6 b/src/migration-scripts/firewall/5-to-6
index ccb86830a..e1eaea7a1 100755
--- a/src/migration-scripts/firewall/5-to-6
+++ b/src/migration-scripts/firewall/5-to-6
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -46,12 +46,54 @@ for interface in config.list_nodes(base):
if config.exists(base + [interface, 'adjust-mss']):
section = Section.section(interface)
tmp = config.return_value(base + [interface, 'adjust-mss'])
- config.set(['interfaces', section, interface, 'ip', 'adjust-mss'], value=tmp)
+
+ vlan = interface.split('.')
+ base_interface_path = ['interfaces', section, vlan[0]]
+
+ if len(vlan) == 1:
+ # Normal interface, no VLAN
+ config.set(base_interface_path + ['ip', 'adjust-mss'], value=tmp)
+ elif len(vlan) == 2:
+ # Regular VIF or VIF-S interface - we need to check the config
+ vif = vlan[1]
+ if config.exists(base_interface_path + ['vif', vif]):
+ config.set(base_interface_path + ['vif', vif, 'ip', 'adjust-mss'], value=tmp)
+ elif config.exists(base_interface_path + ['vif-s', vif]):
+ config.set(base_interface_path + ['vif-s', vif, 'ip', 'adjust-mss'], value=tmp)
+ elif len(vlan) == 3:
+ # VIF-S interface with VIF-C subinterface
+ vif_s = vlan[1]
+ vif_c = vlan[2]
+ config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ip', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif-s'])
+ config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])
if config.exists(base + [interface, 'adjust-mss6']):
section = Section.section(interface)
tmp = config.return_value(base + [interface, 'adjust-mss6'])
- config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp)
+
+ vlan = interface.split('.')
+ base_interface_path = ['interfaces', section, vlan[0]]
+
+ if len(vlan) == 1:
+ # Normal interface, no VLAN
+ config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp)
+ elif len(vlan) == 2:
+ # Regular VIF or VIF-S interface - we need to check the config
+ vif = vlan[1]
+ if config.exists(base_interface_path + ['vif', vif]):
+ config.set(base_interface_path + ['vif', vif, 'ipv6', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif'])
+ elif config.exists(base_interface_path + ['vif-s', vif]):
+ config.set(base_interface_path + ['vif-s', vif, 'ipv6', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif-s'])
+ elif len(vlan) == 3:
+ # VIF-S interface with VIF-C subinterface
+ vif_s = vlan[1]
+ vif_c = vlan[2]
+ config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ipv6', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif-s'])
+ config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])
config.delete(['firewall', 'options'])
diff --git a/src/migration-scripts/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7
index 626d6849f..9ad887acc 100755
--- a/src/migration-scripts/firewall/6-to-7
+++ b/src/migration-scripts/firewall/6-to-7
@@ -28,7 +28,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8
index ce527acf5..d06c3150a 100755
--- a/src/migration-scripts/firewall/7-to-8
+++ b/src/migration-scripts/firewall/7-to-8
@@ -25,7 +25,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/firewall/8-to-9 b/src/migration-scripts/firewall/8-to-9
index f7c1bb90d..d7647354a 100755
--- a/src/migration-scripts/firewall/8-to-9
+++ b/src/migration-scripts/firewall/8-to-9
@@ -28,7 +28,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/firewall/9-to-10 b/src/migration-scripts/firewall/9-to-10
index 6f67cc512..a70460718 100755
--- a/src/migration-scripts/firewall/9-to-10
+++ b/src/migration-scripts/firewall/9-to-10
@@ -28,7 +28,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/flow-accounting/0-to-1 b/src/migration-scripts/flow-accounting/0-to-1
index 72cce77b0..0f790fd9c 100755
--- a/src/migration-scripts/flow-accounting/0-to-1
+++ b/src/migration-scripts/flow-accounting/0-to-1
@@ -21,7 +21,7 @@
from sys import argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/https/2-to-3 b/src/migration-scripts/https/2-to-3
index fa29fdd18..2beba6d2b 100755
--- a/src/migration-scripts/https/2-to-3
+++ b/src/migration-scripts/https/2-to-3
@@ -25,7 +25,7 @@ from vyos.pki import create_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
-if (len(sys.argv) < 2):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/https/3-to-4 b/src/migration-scripts/https/3-to-4
index 5ee528b31..b3cfca201 100755
--- a/src/migration-scripts/https/3-to-4
+++ b/src/migration-scripts/https/3-to-4
@@ -20,7 +20,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 2):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/ids/0-to-1 b/src/migration-scripts/ids/0-to-1
index 9f08f7dc7..8b7850a1a 100755
--- a/src/migration-scripts/ids/0-to-1
+++ b/src/migration-scripts/ids/0-to-1
@@ -19,7 +19,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1
index c7f324661..25f6842eb 100755
--- a/src/migration-scripts/interfaces/0-to-1
+++ b/src/migration-scripts/interfaces/0-to-1
@@ -37,7 +37,7 @@ def migrate_bridge(config, tree, intf):
if __name__ == '__main__':
- if (len(sys.argv) < 1):
+ if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/interfaces/1-to-2 b/src/migration-scripts/interfaces/1-to-2
index c75404d85..c95623c2b 100755
--- a/src/migration-scripts/interfaces/1-to-2
+++ b/src/migration-scripts/interfaces/1-to-2
@@ -7,7 +7,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/interfaces/10-to-11 b/src/migration-scripts/interfaces/10-to-11
index 6b8e49ed9..cafaa3fa4 100755
--- a/src/migration-scripts/interfaces/10-to-11
+++ b/src/migration-scripts/interfaces/10-to-11
@@ -23,7 +23,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/11-to-12 b/src/migration-scripts/interfaces/11-to-12
index 0dad24642..e9eb7f939 100755
--- a/src/migration-scripts/interfaces/11-to-12
+++ b/src/migration-scripts/interfaces/11-to-12
@@ -22,7 +22,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13
index f866ca9a6..ef1d93903 100755
--- a/src/migration-scripts/interfaces/12-to-13
+++ b/src/migration-scripts/interfaces/12-to-13
@@ -24,7 +24,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/13-to-14 b/src/migration-scripts/interfaces/13-to-14
index 6e6439c36..b20d8b4db 100755
--- a/src/migration-scripts/interfaces/13-to-14
+++ b/src/migration-scripts/interfaces/13-to-14
@@ -21,7 +21,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/14-to-15 b/src/migration-scripts/interfaces/14-to-15
index c38db0bf8..e21251f86 100755
--- a/src/migration-scripts/interfaces/14-to-15
+++ b/src/migration-scripts/interfaces/14-to-15
@@ -20,7 +20,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/15-to-16 b/src/migration-scripts/interfaces/15-to-16
index 804c48be0..ae3441b9f 100755
--- a/src/migration-scripts/interfaces/15-to-16
+++ b/src/migration-scripts/interfaces/15-to-16
@@ -20,7 +20,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17
index d123be06f..75f160686 100755
--- a/src/migration-scripts/interfaces/16-to-17
+++ b/src/migration-scripts/interfaces/16-to-17
@@ -21,7 +21,7 @@ import sys
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(sys.argv) < 1):
+ if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
@@ -35,7 +35,7 @@ if __name__ == '__main__':
if not config.exists(base):
# Nothing to do
sys.exit(0)
-
+
for interface in config.list_nodes(base):
mirror_old_base = base + [interface, 'mirror']
if config.exists(mirror_old_base):
@@ -43,7 +43,7 @@ if __name__ == '__main__':
if config.exists(mirror_old_base):
config.delete(mirror_old_base)
config.set(mirror_old_base + ['ingress'],intf[0])
-
+
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
diff --git a/src/migration-scripts/interfaces/17-to-18 b/src/migration-scripts/interfaces/17-to-18
index b8cb8c119..51486ac37 100755
--- a/src/migration-scripts/interfaces/17-to-18
+++ b/src/migration-scripts/interfaces/17-to-18
@@ -22,7 +22,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19
index a12c4a6cd..c3209f250 100755
--- a/src/migration-scripts/interfaces/18-to-19
+++ b/src/migration-scripts/interfaces/18-to-19
@@ -41,7 +41,7 @@ def replace_nat_interfaces(config, old, new):
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20
index e96663e54..05abae898 100755
--- a/src/migration-scripts/interfaces/19-to-20
+++ b/src/migration-scripts/interfaces/19-to-20
@@ -19,7 +19,7 @@ from sys import exit
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/2-to-3 b/src/migration-scripts/interfaces/2-to-3
index 68d41de39..15c3bc8be 100755
--- a/src/migration-scripts/interfaces/2-to-3
+++ b/src/migration-scripts/interfaces/2-to-3
@@ -7,7 +7,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
index cb1c36882..14ad0fe4d 100755
--- a/src/migration-scripts/interfaces/20-to-21
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -23,7 +23,7 @@ from sys import argv
from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22
index 098102102..1838eb1c0 100755
--- a/src/migration-scripts/interfaces/21-to-22
+++ b/src/migration-scripts/interfaces/21-to-22
@@ -17,7 +17,7 @@
from sys import argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/22-to-23 b/src/migration-scripts/interfaces/22-to-23
index 06e07572f..8b21fce51 100755
--- a/src/migration-scripts/interfaces/22-to-23
+++ b/src/migration-scripts/interfaces/22-to-23
@@ -75,7 +75,7 @@ def migrate_ripng(config, path, interface):
config.delete(path[:-1])
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/23-to-24 b/src/migration-scripts/interfaces/23-to-24
index d1ec2ad3e..8fd79ecc6 100755
--- a/src/migration-scripts/interfaces/23-to-24
+++ b/src/migration-scripts/interfaces/23-to-24
@@ -22,7 +22,7 @@ import sys
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(sys.argv) < 1):
+ if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/interfaces/24-to-25 b/src/migration-scripts/interfaces/24-to-25
index 4095f2a3e..9aa6ea5e3 100755
--- a/src/migration-scripts/interfaces/24-to-25
+++ b/src/migration-scripts/interfaces/24-to-25
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,6 +19,7 @@
import os
import sys
+
from vyos.configtree import ConfigTree
from vyos.pki import CERT_BEGIN
from vyos.pki import load_certificate
@@ -29,7 +30,7 @@ from vyos.pki import encode_certificate
from vyos.pki import encode_dh_parameters
from vyos.pki import encode_private_key
from vyos.pki import verify_crl
-from vyos.util import run
+from vyos.utils.process import run
def wrapped_pem_to_config_value(pem):
out = []
@@ -52,7 +53,7 @@ def read_file_for_pki(config_auth_path):
return output
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
@@ -241,7 +242,7 @@ if config.exists(base):
config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
else:
print(f'Failed to migrate private key on openvpn interface {interface}')
-
+
config.delete(x509_base + ['key-file'])
if config.exists(x509_base + ['dh-file']):
@@ -276,7 +277,7 @@ base = ['interfaces', 'wireguard']
if config.exists(base):
for interface in config.list_nodes(base):
private_key_path = base + [interface, 'private-key']
-
+
key_file = 'default'
if config.exists(private_key_path):
key_file = config.return_value(private_key_path)
@@ -375,7 +376,7 @@ if config.exists(base):
config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
else:
print(f'Failed to migrate private key on eapol config for interface {interface}')
-
+
config.delete(x509_base + ['key-file'])
try:
diff --git a/src/migration-scripts/interfaces/25-to-26 b/src/migration-scripts/interfaces/25-to-26
index a8936235e..4967a29fa 100755
--- a/src/migration-scripts/interfaces/25-to-26
+++ b/src/migration-scripts/interfaces/25-to-26
@@ -22,7 +22,7 @@ from sys import argv
from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/26-to-27 b/src/migration-scripts/interfaces/26-to-27
index 949cc55b6..a0d043d11 100755
--- a/src/migration-scripts/interfaces/26-to-27
+++ b/src/migration-scripts/interfaces/26-to-27
@@ -22,7 +22,7 @@ from sys import argv
from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/27-to-28 b/src/migration-scripts/interfaces/27-to-28
index 6225d6414..ad5bfa653 100755
--- a/src/migration-scripts/interfaces/27-to-28
+++ b/src/migration-scripts/interfaces/27-to-28
@@ -22,7 +22,7 @@ from sys import argv
from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -37,7 +37,6 @@ if not config.exists(base):
exit(0)
for ifname in config.list_nodes(base):
- print(ifname)
multicast_base = base + [ifname, 'multicast']
if config.exists(multicast_base):
tmp = config.return_value(multicast_base)
diff --git a/src/migration-scripts/interfaces/28-to-29 b/src/migration-scripts/interfaces/28-to-29
new file mode 100755
index 000000000..acb6ee1fb
--- /dev/null
+++ b/src/migration-scripts/interfaces/28-to-29
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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/>.
+
+# T5286: remove XDP support in favour of VPP
+
+from sys import argv
+
+from vyos.ethtool import Ethtool
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+supports_xdp = ['bonding', 'ethernet']
+config = ConfigTree(config_file)
+
+for if_type in supports_xdp:
+ base = ['interfaces', if_type]
+ if not config.exists(base):
+ continue
+ for interface in config.list_nodes(base):
+ if_base = base + [interface]
+ if config.exists(if_base + ['xdp']):
+ config.delete(if_base + ['xdp'])
+
+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/interfaces/29-to-30 b/src/migration-scripts/interfaces/29-to-30
new file mode 100755
index 000000000..97e1b329c
--- /dev/null
+++ b/src/migration-scripts/interfaces/29-to-30
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2023 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/>.
+#
+# Deletes Wireguard peers if they have the same public key as the router has.
+import sys
+from vyos.configtree import ConfigTree
+from vyos.utils.network import is_wireguard_key_pair
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print("Must specify file name!")
+ sys.exit(1)
+
+ file_name = sys.argv[1]
+
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+ base = ['interfaces', 'wireguard']
+ if not config.exists(base):
+ # Nothing to do
+ sys.exit(0)
+ for interface in config.list_nodes(base):
+ private_key = config.return_value(base + [interface, 'private-key'])
+ interface_base = base + [interface]
+ if config.exists(interface_base + ['peer']):
+ for peer in config.list_nodes(interface_base + ['peer']):
+ peer_base = interface_base + ['peer', peer]
+ peer_public_key = config.return_value(peer_base + ['public-key'])
+ if config.exists(peer_base + ['public-key']):
+ if not config.exists(peer_base + ['disable']) \
+ and is_wireguard_key_pair(private_key, peer_public_key):
+ config.set(peer_base + ['disable'])
+
+ 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))
+ sys.exit(1)
diff --git a/src/migration-scripts/interfaces/3-to-4 b/src/migration-scripts/interfaces/3-to-4
index e3bd25a68..c7fd7d01d 100755
--- a/src/migration-scripts/interfaces/3-to-4
+++ b/src/migration-scripts/interfaces/3-to-4
@@ -6,7 +6,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/interfaces/4-to-5 b/src/migration-scripts/interfaces/4-to-5
index f645c5aeb..68d81e846 100755
--- a/src/migration-scripts/interfaces/4-to-5
+++ b/src/migration-scripts/interfaces/4-to-5
@@ -57,7 +57,7 @@ def migrate_dialer(config, tree, intf):
if __name__ == '__main__':
- if (len(sys.argv) < 1):
+ if len(sys.argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/5-to-6 b/src/migration-scripts/interfaces/5-to-6
index ae79c1d1b..9d9a49c2d 100755
--- a/src/migration-scripts/interfaces/5-to-6
+++ b/src/migration-scripts/interfaces/5-to-6
@@ -98,7 +98,7 @@ def copy_rtradv(c, old_base, interface):
c.delete(new_base + ['link-mtu'])
if __name__ == '__main__':
- if (len(sys.argv) < 1):
+ if len(sys.argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7
index 220c7e601..49b853d90 100755
--- a/src/migration-scripts/interfaces/6-to-7
+++ b/src/migration-scripts/interfaces/6-to-7
@@ -20,7 +20,7 @@ import sys
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(sys.argv) < 1):
+ if len(sys.argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/7-to-8 b/src/migration-scripts/interfaces/7-to-8
index a4051301f..9343a48a8 100755
--- a/src/migration-scripts/interfaces/7-to-8
+++ b/src/migration-scripts/interfaces/7-to-8
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,7 +21,8 @@ import os
from sys import exit, argv
from vyos.configtree import ConfigTree
-from vyos.util import chown, chmod_750
+from vyos.utils.permission import chown
+from vyos.utils.permission import chmod_750
def migrate_default_keys():
kdir = r'/config/auth/wireguard'
@@ -36,7 +37,7 @@ def migrate_default_keys():
os.rename(f'{kdir}/public.key', f'{location}/public.key')
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/8-to-9 b/src/migration-scripts/interfaces/8-to-9
index 2d1efd418..960962be7 100755
--- a/src/migration-scripts/interfaces/8-to-9
+++ b/src/migration-scripts/interfaces/8-to-9
@@ -22,7 +22,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/interfaces/9-to-10 b/src/migration-scripts/interfaces/9-to-10
index 4aa2c42b5..e9b8cb784 100755
--- a/src/migration-scripts/interfaces/9-to-10
+++ b/src/migration-scripts/interfaces/9-to-10
@@ -23,7 +23,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipoe-server/0-to-1 b/src/migration-scripts/ipoe-server/0-to-1
index d768758ba..ac9d13abc 100755
--- a/src/migration-scripts/ipoe-server/0-to-1
+++ b/src/migration-scripts/ipoe-server/0-to-1
@@ -26,7 +26,7 @@ import sys
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/10-to-11 b/src/migration-scripts/ipsec/10-to-11
index 0707a5e3c..509216267 100755
--- a/src/migration-scripts/ipsec/10-to-11
+++ b/src/migration-scripts/ipsec/10-to-11
@@ -20,7 +20,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/11-to-12 b/src/migration-scripts/ipsec/11-to-12
index 8bbde5efa..e34882c23 100755
--- a/src/migration-scripts/ipsec/11-to-12
+++ b/src/migration-scripts/ipsec/11-to-12
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/4-to-5 b/src/migration-scripts/ipsec/4-to-5
index 4e959a7bf..772d05787 100755
--- a/src/migration-scripts/ipsec/4-to-5
+++ b/src/migration-scripts/ipsec/4-to-5
@@ -20,7 +20,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/ipsec/5-to-6 b/src/migration-scripts/ipsec/5-to-6
index 3a8b3926d..7d7c777c6 100755
--- a/src/migration-scripts/ipsec/5-to-6
+++ b/src/migration-scripts/ipsec/5-to-6
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/6-to-7 b/src/migration-scripts/ipsec/6-to-7
index 788a87095..71fbbe8a1 100755
--- a/src/migration-scripts/ipsec/6-to-7
+++ b/src/migration-scripts/ipsec/6-to-7
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,9 +27,9 @@ from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
-from vyos.util import run
+from vyos.utils.process import run
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -127,7 +127,7 @@ if config.exists(ipsec_site_base):
config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem))
else:
print(f'Failed to migrate CRL on peer "{peer}"')
-
+
config.delete(peer_x509_base + ['crl-file'])
if config.exists(peer_x509_base + ['key', 'file']):
@@ -157,7 +157,7 @@ if config.exists(ipsec_site_base):
config.set(peer_x509_base + ['private-key-passphrase'], value=key_passphrase)
else:
print(f'Failed to migrate private key on peer "{peer}"')
-
+
config.delete(peer_x509_base + ['key'])
if changes_made:
diff --git a/src/migration-scripts/ipsec/7-to-8 b/src/migration-scripts/ipsec/7-to-8
index 5d48b2875..e002db0b1 100755
--- a/src/migration-scripts/ipsec/7-to-8
+++ b/src/migration-scripts/ipsec/7-to-8
@@ -31,7 +31,7 @@ from vyos.pki import load_private_key
from vyos.pki import encode_public_key
from vyos.pki import encode_private_key
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/8-to-9 b/src/migration-scripts/ipsec/8-to-9
index eb44b6216..c08411f83 100755
--- a/src/migration-scripts/ipsec/8-to-9
+++ b/src/migration-scripts/ipsec/8-to-9
@@ -19,7 +19,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10
index de366ef3b..a4a71d38e 100755
--- a/src/migration-scripts/ipsec/9-to-10
+++ b/src/migration-scripts/ipsec/9-to-10
@@ -24,7 +24,7 @@ from vyos.template import is_ipv4
from vyos.template import is_ipv6
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/isis/0-to-1 b/src/migration-scripts/isis/0-to-1
index 93cbbbed5..0149c0c1f 100755
--- a/src/migration-scripts/isis/0-to-1
+++ b/src/migration-scripts/isis/0-to-1
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -37,12 +37,9 @@ if not config.exists(base):
# Nothing to do
exit(0)
-# Only one IS-IS process is supported, thus this operation is save
-isis_base = base + config.list_nodes(base)
-
# We need a temporary copy of the config
tmp_base = ['protocols', 'isis2']
-config.copy(isis_base, tmp_base)
+config.copy(base, tmp_base)
# Now it's save to delete the old configuration
config.delete(base)
diff --git a/src/migration-scripts/isis/1-to-2 b/src/migration-scripts/isis/1-to-2
index f914ea995..9c110bf2a 100755
--- a/src/migration-scripts/isis/1-to-2
+++ b/src/migration-scripts/isis/1-to-2
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/isis/2-to-3 b/src/migration-scripts/isis/2-to-3
index 4490feb0a..78e3c1715 100755
--- a/src/migration-scripts/isis/2-to-3
+++ b/src/migration-scripts/isis/2-to-3
@@ -22,7 +22,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/l2tp/0-to-1 b/src/migration-scripts/l2tp/0-to-1
index 686ebc655..15d229822 100755
--- a/src/migration-scripts/l2tp/0-to-1
+++ b/src/migration-scripts/l2tp/0-to-1
@@ -8,7 +8,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/l2tp/1-to-2 b/src/migration-scripts/l2tp/1-to-2
index c46eba8f8..2ffb91c53 100755
--- a/src/migration-scripts/l2tp/1-to-2
+++ b/src/migration-scripts/l2tp/1-to-2
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/l2tp/2-to-3 b/src/migration-scripts/l2tp/2-to-3
index 3472ee3ed..b46b0f22e 100755
--- a/src/migration-scripts/l2tp/2-to-3
+++ b/src/migration-scripts/l2tp/2-to-3
@@ -23,7 +23,7 @@ import sys
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/l2tp/3-to-4 b/src/migration-scripts/l2tp/3-to-4
index 18eabadec..8c2b909b7 100755
--- a/src/migration-scripts/l2tp/3-to-4
+++ b/src/migration-scripts/l2tp/3-to-4
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,9 +27,9 @@ from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
-from vyos.util import run
+from vyos.utils.process import run
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -156,7 +156,7 @@ if config.exists(x509_base + ['server-key-file']):
config.set(x509_base + ['private-key-passphrase'], value=key_passphrase)
else:
print(f'Failed to migrate private key on l2tp remote-access config')
-
+
config.delete(x509_base + ['server-key-file'])
if config.exists(x509_base + ['server-key-password']):
config.delete(x509_base + ['server-key-password'])
diff --git a/src/migration-scripts/lldp/0-to-1 b/src/migration-scripts/lldp/0-to-1
index 5f66570e7..a936cbdfc 100755
--- a/src/migration-scripts/lldp/0-to-1
+++ b/src/migration-scripts/lldp/0-to-1
@@ -7,7 +7,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/monitoring/0-to-1 b/src/migration-scripts/monitoring/0-to-1
index 803cdb49c..384d22f8c 100755
--- a/src/migration-scripts/monitoring/0-to-1
+++ b/src/migration-scripts/monitoring/0-to-1
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/nat/4-to-5 b/src/migration-scripts/nat/4-to-5
index b791996e2..ce215d455 100755
--- a/src/migration-scripts/nat/4-to-5
+++ b/src/migration-scripts/nat/4-to-5
@@ -20,7 +20,7 @@
from sys import argv,exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/nat66/0-to-1 b/src/migration-scripts/nat66/0-to-1
index 83b421926..444b2315f 100755
--- a/src/migration-scripts/nat66/0-to-1
+++ b/src/migration-scripts/nat66/0-to-1
@@ -17,7 +17,7 @@
from sys import argv,exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ntp/0-to-1 b/src/migration-scripts/ntp/0-to-1
index 294964580..cbce45b9b 100755
--- a/src/migration-scripts/ntp/0-to-1
+++ b/src/migration-scripts/ntp/0-to-1
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/ntp/1-to-2 b/src/migration-scripts/ntp/1-to-2
index d1e510e4c..fd1f15d91 100755
--- a/src/migration-scripts/ntp/1-to-2
+++ b/src/migration-scripts/ntp/1-to-2
@@ -20,7 +20,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/ntp/2-to-3 b/src/migration-scripts/ntp/2-to-3
new file mode 100755
index 000000000..a4351845e
--- /dev/null
+++ b/src/migration-scripts/ntp/2-to-3
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2023 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/>.
+
+# T5154: allow only one ip address per family for parameter 'listen-address'
+# Allow only one interface for parameter 'interface'
+# If more than one are specified, remove such entries
+
+import sys
+
+from vyos.configtree import ConfigTree
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
+
+if len(sys.argv) < 2:
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+base_path = ['service', 'ntp']
+if not config.exists(base_path):
+ # Nothing to do
+ sys.exit(0)
+
+if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv4(addr)]) > 1):
+ for addr in config.return_values(base_path + ['listen-address']):
+ if is_ipv4(addr):
+ config.delete_value(base_path + ['listen-address'], addr)
+
+if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv6(addr)]) > 1):
+ for addr in config.return_values(base_path + ['listen-address']):
+ if is_ipv6(addr):
+ config.delete_value(base_path + ['listen-address'], addr)
+
+if config.exists(base_path + ['interface']):
+ if len(config.return_values(base_path + ['interface'])) > 1:
+ config.delete(base_path + ['interface'])
+
+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))
+ sys.exit(1)
diff --git a/src/migration-scripts/openconnect/0-to-1 b/src/migration-scripts/openconnect/0-to-1
index 83cd09143..8be15fad1 100755
--- a/src/migration-scripts/openconnect/0-to-1
+++ b/src/migration-scripts/openconnect/0-to-1
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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
@@ -26,9 +26,9 @@ from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
-from vyos.util import run
+from vyos.utils.process import run
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -125,7 +125,7 @@ if config.exists(x509_base + ['key-file']):
config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
else:
print(f'Failed to migrate private key on openconnect config')
-
+
config.delete(x509_base + ['key-file'])
try:
diff --git a/src/migration-scripts/openconnect/1-to-2 b/src/migration-scripts/openconnect/1-to-2
index 7031fb252..7978aa56e 100755
--- a/src/migration-scripts/openconnect/1-to-2
+++ b/src/migration-scripts/openconnect/1-to-2
@@ -20,7 +20,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
@@ -39,13 +39,13 @@ if not config.exists(cfg_base):
else:
if config.exists(cfg_base + ['authentication', 'mode']):
if config.return_value(cfg_base + ['authentication', 'mode']) == 'radius':
- # if "mode value radius", change to "tag node mode + valueless node radius"
- config.delete(cfg_base + ['authentication','mode', 'radius'])
- config.set(cfg_base + ['authentication', 'mode', 'radius'], value=None, replace=True)
- elif not config.exists(cfg_base + ['authentication', 'mode', 'local']):
- # if "mode local", change to "tag node mode + node local value password"
- config.delete(cfg_base + ['authentication', 'mode', 'local'])
- config.set(cfg_base + ['authentication', 'mode', 'local'], value='password', replace=True)
+ # if "mode value radius", change to "mode + valueless node radius"
+ config.delete_value(cfg_base + ['authentication','mode'], 'radius')
+ config.set(cfg_base + ['authentication', 'mode', 'radius'], value=None)
+ elif config.return_value(cfg_base + ['authentication', 'mode']) == 'local':
+ # if "mode local", change to "mode + node local value password"
+ config.delete_value(cfg_base + ['authentication', 'mode'], 'local')
+ config.set(cfg_base + ['authentication', 'mode', 'local'], value='password')
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
diff --git a/src/migration-scripts/ospf/0-to-1 b/src/migration-scripts/ospf/0-to-1
index 678569d9e..8f02acada 100755
--- a/src/migration-scripts/ospf/0-to-1
+++ b/src/migration-scripts/ospf/0-to-1
@@ -37,7 +37,7 @@ def ospf_passive_migration(config, ospf_base):
config.set(ospf_base + ['interface', interface, 'passive', 'disable'])
config.delete(ospf_base + ['passive-interface-exclude'])
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ospf/1-to-2 b/src/migration-scripts/ospf/1-to-2
index a6beaf04e..ba9499c60 100755
--- a/src/migration-scripts/ospf/1-to-2
+++ b/src/migration-scripts/ospf/1-to-2
@@ -22,7 +22,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/policy/0-to-1 b/src/migration-scripts/policy/0-to-1
index 7134920ad..8508b734a 100755
--- a/src/migration-scripts/policy/0-to-1
+++ b/src/migration-scripts/policy/0-to-1
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/policy/1-to-2 b/src/migration-scripts/policy/1-to-2
index eebbf9d41..c70490ce9 100755
--- a/src/migration-scripts/policy/1-to-2
+++ b/src/migration-scripts/policy/1-to-2
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/policy/2-to-3 b/src/migration-scripts/policy/2-to-3
index 84cb1ff4a..8a62c8e6f 100755
--- a/src/migration-scripts/policy/2-to-3
+++ b/src/migration-scripts/policy/2-to-3
@@ -23,7 +23,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/policy/3-to-4 b/src/migration-scripts/policy/3-to-4
index bae30cffc..1ebb248b0 100755
--- a/src/migration-scripts/policy/3-to-4
+++ b/src/migration-scripts/policy/3-to-4
@@ -51,7 +51,7 @@ def community_migrate(config: ConfigTree, rule: list[str]) -> bool:
:rtype: bool
"""
community_list = list((config.return_value(rule)).split(" "))
- config.delete(rule)
+
if 'none' in community_list:
config.set(rule + ['none'])
return False
@@ -61,8 +61,10 @@ def community_migrate(config: ConfigTree, rule: list[str]) -> bool:
community_action = 'add'
community_list.remove('additive')
for community in community_list:
- config.set(rule + [community_action], value=community,
- replace=False)
+ if len(community):
+ config.set(rule + [community_action], value=community,
+ replace=False)
+ config.delete(rule)
if community_action == 'replace':
return False
else:
@@ -96,7 +98,7 @@ def extcommunity_migrate(config: ConfigTree, rule: list[str]) -> None:
config.set(rule + ['soo'], value=community, replace=False)
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5
index 33c9e6ade..f6f889c35 100755
--- a/src/migration-scripts/policy/4-to-5
+++ b/src/migration-scripts/policy/4-to-5
@@ -24,7 +24,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/0-to-1 b/src/migration-scripts/pppoe-server/0-to-1
index 063c7eb56..4d36f8545 100755
--- a/src/migration-scripts/pppoe-server/0-to-1
+++ b/src/migration-scripts/pppoe-server/0-to-1
@@ -20,7 +20,7 @@
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/1-to-2 b/src/migration-scripts/pppoe-server/1-to-2
index 902efb86b..c73899ca1 100755
--- a/src/migration-scripts/pppoe-server/1-to-2
+++ b/src/migration-scripts/pppoe-server/1-to-2
@@ -21,7 +21,7 @@ import os
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/2-to-3 b/src/migration-scripts/pppoe-server/2-to-3
index 7cae3b5bc..a7be060df 100755
--- a/src/migration-scripts/pppoe-server/2-to-3
+++ b/src/migration-scripts/pppoe-server/2-to-3
@@ -19,7 +19,7 @@
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/3-to-4 b/src/migration-scripts/pppoe-server/3-to-4
index 5f9730a41..c07bbb1df 100755
--- a/src/migration-scripts/pppoe-server/3-to-4
+++ b/src/migration-scripts/pppoe-server/3-to-4
@@ -21,7 +21,7 @@ import os
from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/4-to-5 b/src/migration-scripts/pppoe-server/4-to-5
index 05e9c17d6..5850db673 100755
--- a/src/migration-scripts/pppoe-server/4-to-5
+++ b/src/migration-scripts/pppoe-server/4-to-5
@@ -20,7 +20,7 @@ from vyos.configtree import ConfigTree
from sys import argv
from sys import exit
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/5-to-6 b/src/migration-scripts/pppoe-server/5-to-6
index e4888f4db..e079ae684 100755
--- a/src/migration-scripts/pppoe-server/5-to-6
+++ b/src/migration-scripts/pppoe-server/5-to-6
@@ -20,7 +20,7 @@ from vyos.configtree import ConfigTree
from sys import argv
from sys import exit
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pptp/0-to-1 b/src/migration-scripts/pptp/0-to-1
index d0c7a83b5..1b7697c11 100755
--- a/src/migration-scripts/pptp/0-to-1
+++ b/src/migration-scripts/pptp/0-to-1
@@ -8,7 +8,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/pptp/1-to-2 b/src/migration-scripts/pptp/1-to-2
index a13cc3a4f..99624dceb 100755
--- a/src/migration-scripts/pptp/1-to-2
+++ b/src/migration-scripts/pptp/1-to-2
@@ -21,7 +21,7 @@ from sys import argv, exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/qos/1-to-2 b/src/migration-scripts/qos/1-to-2
index 14d3a6e0a..cca32d06e 100755
--- a/src/migration-scripts/qos/1-to-2
+++ b/src/migration-scripts/qos/1-to-2
@@ -18,7 +18,7 @@ from sys import argv,exit
from vyos.base import Warning
from vyos.configtree import ConfigTree
-from vyos.util import read_file
+from vyos.utils.file import read_file
def bandwidth_percent_to_val(interface, percent) -> int:
speed = read_file(f'/sys/class/net/{interface}/speed')
@@ -28,7 +28,7 @@ def bandwidth_percent_to_val(interface, percent) -> int:
speed = int(speed) *1000000 # convert to MBit/s
return speed * int(percent) // 100 # integer division
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/quagga/10-to-11 b/src/migration-scripts/quagga/10-to-11
index 04fc16f79..0ed4f5df6 100755
--- a/src/migration-scripts/quagga/10-to-11
+++ b/src/migration-scripts/quagga/10-to-11
@@ -22,7 +22,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/quagga/2-to-3 b/src/migration-scripts/quagga/2-to-3
index 4c1cd86a3..96b56da70 100755
--- a/src/migration-scripts/quagga/2-to-3
+++ b/src/migration-scripts/quagga/2-to-3
@@ -21,7 +21,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/quagga/3-to-4 b/src/migration-scripts/quagga/3-to-4
index be3528391..1e8c8e2f2 100755
--- a/src/migration-scripts/quagga/3-to-4
+++ b/src/migration-scripts/quagga/3-to-4
@@ -28,7 +28,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/quagga/4-to-5 b/src/migration-scripts/quagga/4-to-5
index f8c87ce8c..fcb496a9c 100755
--- a/src/migration-scripts/quagga/4-to-5
+++ b/src/migration-scripts/quagga/4-to-5
@@ -21,7 +21,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/quagga/5-to-6 b/src/migration-scripts/quagga/5-to-6
index a71851942..f075fc2e7 100755
--- a/src/migration-scripts/quagga/5-to-6
+++ b/src/migration-scripts/quagga/5-to-6
@@ -22,7 +22,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 2):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7
index 25cf5eebd..ed295a95c 100755
--- a/src/migration-scripts/quagga/6-to-7
+++ b/src/migration-scripts/quagga/6-to-7
@@ -23,7 +23,7 @@ from vyos.configtree import ConfigTree
from vyos.template import is_ipv4
from vyos.template import is_ipv6
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/quagga/7-to-8 b/src/migration-scripts/quagga/7-to-8
index 15c44924f..8f11bf390 100755
--- a/src/migration-scripts/quagga/7-to-8
+++ b/src/migration-scripts/quagga/7-to-8
@@ -22,7 +22,7 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/quagga/8-to-9 b/src/migration-scripts/quagga/8-to-9
index 38507bd3d..0f683d5a1 100755
--- a/src/migration-scripts/quagga/8-to-9
+++ b/src/migration-scripts/quagga/8-to-9
@@ -84,7 +84,7 @@ def migrate_route(config, base, path, route_route6):
config.rename(vrf_path, 'vrf')
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/quagga/9-to-10 b/src/migration-scripts/quagga/9-to-10
index 249738822..3731762f7 100755
--- a/src/migration-scripts/quagga/9-to-10
+++ b/src/migration-scripts/quagga/9-to-10
@@ -21,7 +21,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/rip/0-to-1 b/src/migration-scripts/rip/0-to-1
index 60d510001..08a866374 100755
--- a/src/migration-scripts/rip/0-to-1
+++ b/src/migration-scripts/rip/0-to-1
@@ -22,7 +22,7 @@ from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/rpki/0-to-1 b/src/migration-scripts/rpki/0-to-1
index 5b4893205..a7b5d07d5 100755
--- a/src/migration-scripts/rpki/0-to-1
+++ b/src/migration-scripts/rpki/0-to-1
@@ -18,7 +18,7 @@ from sys import exit
from sys import argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/salt/0-to-1 b/src/migration-scripts/salt/0-to-1
index 79053c056..481d9de8f 100755
--- a/src/migration-scripts/salt/0-to-1
+++ b/src/migration-scripts/salt/0-to-1
@@ -22,7 +22,7 @@ from sys import argv,exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/snmp/0-to-1 b/src/migration-scripts/snmp/0-to-1
index 096ba779d..b1e61b958 100755
--- a/src/migration-scripts/snmp/0-to-1
+++ b/src/migration-scripts/snmp/0-to-1
@@ -17,7 +17,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/snmp/1-to-2 b/src/migration-scripts/snmp/1-to-2
index 466a624e6..e02cd1aa1 100755
--- a/src/migration-scripts/snmp/1-to-2
+++ b/src/migration-scripts/snmp/1-to-2
@@ -43,7 +43,7 @@ def migrate_keys(config, path):
config.set(config_path_priv, value=tmp)
if __name__ == '__main__':
- if (len(argv) < 1):
+ if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/snmp/2-to-3 b/src/migration-scripts/snmp/2-to-3
index 5f8d9c88d..30911aa27 100755
--- a/src/migration-scripts/snmp/2-to-3
+++ b/src/migration-scripts/snmp/2-to-3
@@ -28,7 +28,7 @@ from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import Section
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ssh/0-to-1 b/src/migration-scripts/ssh/0-to-1
index 91b832276..2595599ac 100755
--- a/src/migration-scripts/ssh/0-to-1
+++ b/src/migration-scripts/ssh/0-to-1
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/ssh/1-to-2 b/src/migration-scripts/ssh/1-to-2
index 31c40df16..79d65d7d4 100755
--- a/src/migration-scripts/ssh/1-to-2
+++ b/src/migration-scripts/ssh/1-to-2
@@ -21,7 +21,7 @@
from sys import argv,exit
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/sstp/0-to-1 b/src/migration-scripts/sstp/0-to-1
index dc65bdeab..e2fe1ea8f 100755
--- a/src/migration-scripts/sstp/0-to-1
+++ b/src/migration-scripts/sstp/0-to-1
@@ -28,7 +28,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/sstp/1-to-2 b/src/migration-scripts/sstp/1-to-2
index 94cb04831..f7ecbb6d4 100755
--- a/src/migration-scripts/sstp/1-to-2
+++ b/src/migration-scripts/sstp/1-to-2
@@ -25,7 +25,7 @@ from shutil import copy2
from stat import S_IRUSR, S_IWUSR, S_IRGRP, S_IROTH
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/sstp/2-to-3 b/src/migration-scripts/sstp/2-to-3
index 963b2ba4b..245db7ad6 100755
--- a/src/migration-scripts/sstp/2-to-3
+++ b/src/migration-scripts/sstp/2-to-3
@@ -21,7 +21,7 @@ from vyos.configtree import ConfigTree
from sys import argv
from sys import exit
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/sstp/3-to-4 b/src/migration-scripts/sstp/3-to-4
index 0568f043f..00ca7a52d 100755
--- a/src/migration-scripts/sstp/3-to-4
+++ b/src/migration-scripts/sstp/3-to-4
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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
@@ -26,9 +26,9 @@ from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
-from vyos.util import run
+from vyos.utils.process import run
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -125,7 +125,7 @@ if config.exists(x509_base + ['key-file']):
config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
else:
print(f'Failed to migrate private key on sstp config')
-
+
config.delete(x509_base + ['key-file'])
try:
diff --git a/src/migration-scripts/system/10-to-11 b/src/migration-scripts/system/10-to-11
index 3c49f0d95..5d662af40 100755
--- a/src/migration-scripts/system/10-to-11
+++ b/src/migration-scripts/system/10-to-11
@@ -6,7 +6,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/11-to-12 b/src/migration-scripts/system/11-to-12
index 9cddaa1a7..880ab56dc 100755
--- a/src/migration-scripts/system/11-to-12
+++ b/src/migration-scripts/system/11-to-12
@@ -8,7 +8,7 @@
import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/12-to-13 b/src/migration-scripts/system/12-to-13
index 36311a19d..e6c4e3802 100755
--- a/src/migration-scripts/system/12-to-13
+++ b/src/migration-scripts/system/12-to-13
@@ -8,7 +8,7 @@ import re
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/13-to-14 b/src/migration-scripts/system/13-to-14
index 5b068f4fc..1fa781869 100755
--- a/src/migration-scripts/system/13-to-14
+++ b/src/migration-scripts/system/13-to-14
@@ -12,10 +12,10 @@ import re
import sys
from vyos.configtree import ConfigTree
-from vyos.util import cmd
+from vyos.utils.process import cmd
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/14-to-15 b/src/migration-scripts/system/14-to-15
index c055dad1f..feaac37de 100755
--- a/src/migration-scripts/system/14-to-15
+++ b/src/migration-scripts/system/14-to-15
@@ -11,7 +11,7 @@ ipv6_blacklist_file = '/etc/modprobe.d/vyatta_blacklist_ipv6.conf'
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/15-to-16 b/src/migration-scripts/system/15-to-16
index 2491e3d0d..aa1c34032 100755
--- a/src/migration-scripts/system/15-to-16
+++ b/src/migration-scripts/system/15-to-16
@@ -7,7 +7,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17
index e70893d55..37e02611d 100755
--- a/src/migration-scripts/system/16-to-17
+++ b/src/migration-scripts/system/16-to-17
@@ -25,7 +25,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/17-to-18 b/src/migration-scripts/system/17-to-18
index 8f762c0e2..f6adebb06 100755
--- a/src/migration-scripts/system/17-to-18
+++ b/src/migration-scripts/system/17-to-18
@@ -22,7 +22,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/18-to-19 b/src/migration-scripts/system/18-to-19
index fd0e15d42..fad1d17a4 100755
--- a/src/migration-scripts/system/18-to-19
+++ b/src/migration-scripts/system/18-to-19
@@ -24,7 +24,7 @@ from sys import argv, exit
from vyos.ifconfig import Interface
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -92,9 +92,6 @@ else:
for intf in dhcp_interfaces:
config.set(base + ['name-servers-dhcp'], value=intf, replace=False)
- # delete old node
- config.delete(base + ['disable-dhcp-nameservers'])
-
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
diff --git a/src/migration-scripts/system/19-to-20 b/src/migration-scripts/system/19-to-20
index eb20fd8db..c04e6a5a6 100755
--- a/src/migration-scripts/system/19-to-20
+++ b/src/migration-scripts/system/19-to-20
@@ -21,7 +21,7 @@ import os
from sys import exit, argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21
index 1728995de..4bcf4edab 100755
--- a/src/migration-scripts/system/20-to-21
+++ b/src/migration-scripts/system/20-to-21
@@ -21,7 +21,7 @@ import os
from sys import argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/21-to-22 b/src/migration-scripts/system/21-to-22
index ad41be646..810b634ab 100755
--- a/src/migration-scripts/system/21-to-22
+++ b/src/migration-scripts/system/21-to-22
@@ -19,7 +19,7 @@ import os
from sys import exit, argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/22-to-23 b/src/migration-scripts/system/22-to-23
index 7f832e48a..8ed198383 100755
--- a/src/migration-scripts/system/22-to-23
+++ b/src/migration-scripts/system/22-to-23
@@ -19,7 +19,7 @@ import os
from sys import exit, argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/23-to-24 b/src/migration-scripts/system/23-to-24
index 97fe82462..fd68dbf22 100755
--- a/src/migration-scripts/system/23-to-24
+++ b/src/migration-scripts/system/23-to-24
@@ -22,7 +22,7 @@ from sys import exit, argv
from vyos.configtree import ConfigTree
from vyos.template import is_ipv4
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/24-to-25 b/src/migration-scripts/system/24-to-25
index c2f70689d..1c81a76e7 100755
--- a/src/migration-scripts/system/24-to-25
+++ b/src/migration-scripts/system/24-to-25
@@ -19,7 +19,7 @@
from sys import exit, argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/25-to-26 b/src/migration-scripts/system/25-to-26
index 615274430..7bdf3be98 100755
--- a/src/migration-scripts/system/25-to-26
+++ b/src/migration-scripts/system/25-to-26
@@ -21,7 +21,7 @@
from sys import exit, argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/6-to-7 b/src/migration-scripts/system/6-to-7
index bf07abf3a..d24521134 100755
--- a/src/migration-scripts/system/6-to-7
+++ b/src/migration-scripts/system/6-to-7
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/7-to-8 b/src/migration-scripts/system/7-to-8
index 4cbb21f17..5d084d2bf 100755
--- a/src/migration-scripts/system/7-to-8
+++ b/src/migration-scripts/system/7-to-8
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/system/8-to-9 b/src/migration-scripts/system/8-to-9
index db3fefdea..e3bb2bca8 100755
--- a/src/migration-scripts/system/8-to-9
+++ b/src/migration-scripts/system/8-to-9
@@ -6,7 +6,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/vrf/0-to-1 b/src/migration-scripts/vrf/0-to-1
index 5df751113..8187138d9 100755
--- a/src/migration-scripts/vrf/0-to-1
+++ b/src/migration-scripts/vrf/0-to-1
@@ -20,7 +20,7 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/vrf/1-to-2 b/src/migration-scripts/vrf/1-to-2
index 9bc704e02..52d4c2c7b 100755
--- a/src/migration-scripts/vrf/1-to-2
+++ b/src/migration-scripts/vrf/1-to-2
@@ -20,7 +20,7 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/vrf/2-to-3 b/src/migration-scripts/vrf/2-to-3
index 8e0f97141..d45b185ee 100755
--- a/src/migration-scripts/vrf/2-to-3
+++ b/src/migration-scripts/vrf/2-to-3
@@ -69,7 +69,7 @@ def _search_tables(config_commands, table_num):
return table_items
-if (len(argv) < 2):
+if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/vrrp/1-to-2 b/src/migration-scripts/vrrp/1-to-2
index b2e61dd38..dba5af81c 100755
--- a/src/migration-scripts/vrrp/1-to-2
+++ b/src/migration-scripts/vrrp/1-to-2
@@ -21,7 +21,7 @@ import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/migration-scripts/vrrp/2-to-3 b/src/migration-scripts/vrrp/2-to-3
index 1151ae18c..ed583b489 100755
--- a/src/migration-scripts/vrrp/2-to-3
+++ b/src/migration-scripts/vrrp/2-to-3
@@ -19,7 +19,7 @@
from sys import argv
from vyos.configtree import ConfigTree
-if (len(argv) < 1):
+if len(argv) < 2:
print('Must specify file name!')
exit(1)
diff --git a/src/migration-scripts/vrrp/3-to-4 b/src/migration-scripts/vrrp/3-to-4
new file mode 100755
index 000000000..e5d93578c
--- /dev/null
+++ b/src/migration-scripts/vrrp/3-to-4
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from sys import argv
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print('Must specify file name!')
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['high-availability', 'virtual-server']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+if config.exists(base):
+ for vs in config.list_nodes(base):
+ vs_base = base + [vs]
+
+ # If the fwmark is used, the address is not required
+ if not config.exists(vs_base + ['fwmark']):
+ # add option: 'virtual-server <tag> address x.x.x.x'
+ config.set(vs_base + ['address'], value=vs)
+
+
+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/webproxy/1-to-2 b/src/migration-scripts/webproxy/1-to-2
index 070ff356d..03f357878 100755
--- a/src/migration-scripts/webproxy/1-to-2
+++ b/src/migration-scripts/webproxy/1-to-2
@@ -7,7 +7,7 @@ import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 1:
+if len(sys.argv) < 2:
print("Must specify file name!")
sys.exit(1)
diff --git a/src/op_mode/accelppp.py b/src/op_mode/accelppp.py
index 00de45fc8..67ce786d0 100755
--- a/src/op_mode/accelppp.py
+++ b/src/op_mode/accelppp.py
@@ -21,7 +21,7 @@ import vyos.accel_ppp
import vyos.opmode
from vyos.configquery import ConfigTreeQuery
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
accel_dict = {
diff --git a/src/op_mode/bgp.py b/src/op_mode/bgp.py
index af9ea788b..096113cb4 100755
--- a/src/op_mode/bgp.py
+++ b/src/op_mode/bgp.py
@@ -81,7 +81,7 @@ ArgFamily = typing.Literal['inet', 'inet6', 'l2vpn']
ArgFamilyModifier = typing.Literal['unicast', 'labeled_unicast', 'multicast', 'vpn', 'flowspec']
def show_summary(raw: bool):
- from vyos.util import cmd
+ from vyos.utils.process import cmd
if raw:
from json import loads
@@ -96,7 +96,7 @@ def show_summary(raw: bool):
return output
def show_neighbors(raw: bool):
- from vyos.util import cmd
+ from vyos.utils.process import cmd
from vyos.utils.dict import dict_to_list
if raw:
@@ -129,7 +129,7 @@ def show(raw: bool,
frr_command = frr_command_template.render(kwargs)
frr_command = re.sub(r'\s+', ' ', frr_command)
- from vyos.util import cmd
+ from vyos.utils.process import cmd
output = cmd(f"vtysh -c '{frr_command}'")
if raw:
diff --git a/src/op_mode/bridge.py b/src/op_mode/bridge.py
index d6098c158..185db4f20 100755
--- a/src/op_mode/bridge.py
+++ b/src/op_mode/bridge.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -22,12 +22,13 @@ import typing
from sys import exit
from tabulate import tabulate
-from vyos.util import cmd, rc_cmd
-from vyos.util import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.process import rc_cmd
+from vyos.utils.process import call
+from vyos.utils.dict import dict_search
import vyos.opmode
-
def _get_json_data():
"""
Get bridge data format JSON
@@ -44,11 +45,14 @@ def _get_raw_data_summary():
return data_dict
-def _get_raw_data_vlan():
+def _get_raw_data_vlan(tunnel:bool=False):
"""
:returns dict
"""
- json_data = cmd('bridge --json --compressvlans vlan show')
+ show = 'show'
+ if tunnel:
+ show = 'tunnel'
+ json_data = cmd(f'bridge --json --compressvlans vlan {show}')
data_dict = json.loads(json_data)
return data_dict
@@ -128,13 +132,38 @@ def _get_formatted_output_vlan(data):
if vlan_entry.get('vlanEnd'):
vlan_end = vlan_entry.get('vlanEnd')
vlan = f'{vlan}-{vlan_end}'
- flags = ', '.join(vlan_entry.get('flags')).lower()
+ flags_raw = vlan_entry.get('flags')
+ flags = ', '.join(flags_raw if isinstance(flags_raw,list) else "").lower()
data_entries.append([interface, vlan, flags])
- headers = ["Interface", "Vlan", "Flags"]
+ headers = ["Interface", "VLAN", "Flags"]
output = tabulate(data_entries, headers)
return output
+def _get_formatted_output_vlan_tunnel(data):
+ data_entries = []
+ for entry in data:
+ interface = entry.get('ifname')
+ first = True
+ for tunnel_entry in entry.get('tunnels'):
+ vlan = tunnel_entry.get('vlan')
+ vni = tunnel_entry.get('tunid')
+ if first:
+ data_entries.append([interface, vlan, vni])
+ first = False
+ else:
+ # Group by VXLAN interface only - no need to repeat
+ # VXLAN interface name for every VLAN <-> VNI mapping
+ #
+ # Interface VLAN VNI
+ # ----------- ------ -----
+ # vxlan0 100 100
+ # 200 200
+ data_entries.append(['', vlan, vni])
+
+ headers = ["Interface", "VLAN", "VNI"]
+ output = tabulate(data_entries, headers)
+ return output
def _get_formatted_output_fdb(data):
data_entries = []
@@ -163,6 +192,23 @@ def _get_formatted_output_mdb(data):
output = tabulate(data_entries, headers)
return output
+def _get_bridge_detail(iface):
+ """Get interface detail statistics"""
+ return call(f'vtysh -c "show interface {iface}"')
+
+def _get_bridge_detail_nexthop_group(iface):
+ """Get interface detail nexthop_group statistics"""
+ return call(f'vtysh -c "show interface {iface} nexthop-group"')
+
+def _get_bridge_detail_nexthop_group_raw(iface):
+ out = cmd(f'vtysh -c "show interface {iface} nexthop-group"')
+ return out
+
+def _get_bridge_detail_raw(iface):
+ """Get interface detail json statistics"""
+ data = cmd(f'vtysh -c "show interface {iface} json"')
+ data_dict = json.loads(data)
+ return data_dict
def show(raw: bool):
bridge_data = _get_raw_data_summary()
@@ -172,12 +218,15 @@ def show(raw: bool):
return _get_formatted_output_summary(bridge_data)
-def show_vlan(raw: bool):
- bridge_vlan = _get_raw_data_vlan()
+def show_vlan(raw: bool, tunnel: typing.Optional[bool]):
+ bridge_vlan = _get_raw_data_vlan(tunnel)
if raw:
return bridge_vlan
else:
- return _get_formatted_output_vlan(bridge_vlan)
+ if tunnel:
+ return _get_formatted_output_vlan_tunnel(bridge_vlan)
+ else:
+ return _get_formatted_output_vlan(bridge_vlan)
def show_fdb(raw: bool, interface: str):
@@ -195,6 +244,17 @@ def show_mdb(raw: bool, interface: str):
else:
return _get_formatted_output_mdb(mdb_data)
+def show_detail(raw: bool, nexthop_group: typing.Optional[bool], interface: str):
+ if raw:
+ if nexthop_group:
+ return _get_bridge_detail_nexthop_group_raw(interface)
+ else:
+ return _get_bridge_detail_raw(interface)
+ else:
+ if nexthop_group:
+ return _get_bridge_detail_nexthop_group(interface)
+ else:
+ return _get_bridge_detail(interface)
if __name__ == '__main__':
try:
diff --git a/src/op_mode/clear_conntrack.py b/src/op_mode/clear_conntrack.py
index 423694187..fec7cf144 100755
--- a/src/op_mode/clear_conntrack.py
+++ b/src/op_mode/clear_conntrack.py
@@ -16,8 +16,9 @@
import sys
-from vyos.util import ask_yes_no
-from vyos.util import cmd, DEVNULL
+from vyos.utils.io import ask_yes_no
+from vyos.utils.process import cmd
+from vyos.utils.process import DEVNULL
if not ask_yes_no("This will clear all currently tracked and expected connections. Continue?"):
sys.exit(1)
diff --git a/src/op_mode/clear_dhcp_lease.py b/src/op_mode/clear_dhcp_lease.py
index 250dbcce1..f372d3af0 100755
--- a/src/op_mode/clear_dhcp_lease.py
+++ b/src/op_mode/clear_dhcp_lease.py
@@ -7,9 +7,9 @@ from isc_dhcp_leases import Lease
from isc_dhcp_leases import IscDhcpLeases
from vyos.configquery import ConfigTreeQuery
-from vyos.util import ask_yes_no
-from vyos.util import call
-from vyos.util import commit_in_progress
+from vyos.utils.io import ask_yes_no
+from vyos.utils.process import call
+from vyos.utils.commit import commit_in_progress
config = ConfigTreeQuery()
diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py
index d39e88bf3..89f929be7 100755
--- a/src/op_mode/connect_disconnect.py
+++ b/src/op_mode/connect_disconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,10 +19,10 @@ import argparse
from psutil import process_iter
-from vyos.util import call
-from vyos.util import commit_in_progress
-from vyos.util import DEVNULL
-from vyos.util import is_wwan_connected
+from vyos.utils.process import call
+from vyos.utils.commit import commit_in_progress
+from vyos.utils.network import is_wwan_connected
+from vyos.utils.process import DEVNULL
def check_ppp_interface(interface):
if not os.path.isfile(f'/etc/ppp/peers/{interface}'):
diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py
index ea7c4c208..cf8adf795 100755
--- a/src/op_mode/conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,8 +19,8 @@ import typing
import xmltodict
from tabulate import tabulate
-from vyos.util import cmd
-from vyos.util import run
+from vyos.utils.process import cmd
+from vyos.utils.process import run
import vyos.opmode
diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py
index c3345a936..a38688e45 100755
--- a/src/op_mode/conntrack_sync.py
+++ b/src/op_mode/conntrack_sync.py
@@ -24,10 +24,10 @@ import vyos.opmode
from argparse import ArgumentParser
from vyos.configquery import CliShellApiConfigQuery
from vyos.configquery import ConfigTreeQuery
-from vyos.util import call
-from vyos.util import commit_in_progress
-from vyos.util import cmd
-from vyos.util import run
+from vyos.utils.commit import commit_in_progress
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.process import run
from vyos.template import render_to_string
conntrackd_bin = '/usr/sbin/conntrackd'
diff --git a/src/op_mode/container.py b/src/op_mode/container.py
index d48766a0c..5a022d0c0 100755
--- a/src/op_mode/container.py
+++ b/src/op_mode/container.py
@@ -19,7 +19,7 @@ import sys
from sys import exit
-from vyos.util import cmd
+from vyos.utils.process import cmd
import vyos.opmode
@@ -36,14 +36,14 @@ def _get_raw_data(command: str) -> list:
return data
def add_image(name: str):
- from vyos.util import rc_cmd
+ from vyos.utils.process import rc_cmd
rc, output = rc_cmd(f'podman image pull {name}')
if rc != 0:
raise vyos.opmode.InternalError(output)
def delete_image(name: str):
- from vyos.util import rc_cmd
+ from vyos.utils.process import rc_cmd
rc, output = rc_cmd(f'podman image rm --force {name}')
if rc != 0:
@@ -77,13 +77,13 @@ def show_network(raw: bool):
def restart(name: str):
- from vyos.util import rc_cmd
+ from vyos.utils.process import rc_cmd
rc, output = rc_cmd(f'systemctl restart vyos-container-{name}.service')
if rc != 0:
print(output)
return None
- print(f'Container name "{name}" restarted!')
+ print(f'Container "{name}" restarted!')
return output
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index fe7f252ba..77f38992b 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -14,10 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import os
import sys
import typing
from datetime import datetime
+from glob import glob
from ipaddress import ip_address
from isc_dhcp_leases import IscDhcpLeases
from tabulate import tabulate
@@ -27,9 +29,12 @@ import vyos.opmode
from vyos.base import Warning
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
-from vyos.util import dict_search
-from vyos.util import is_systemd_service_running
+from vyos.utils.dict import dict_search
+from vyos.utils.file import read_file
+from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_running
+
+time_string = "%a %b %d %H:%M:%S %Z %Y"
config = ConfigTreeQuery()
lease_valid_states = ['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup']
@@ -287,6 +292,97 @@ def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],
return _get_formatted_server_leases(lease_data, family=family)
+def _get_raw_client_leases(family='inet', interface=None):
+ from time import mktime
+ from datetime import datetime
+ from vyos.defaults import directories
+ from vyos.utils.network import get_interface_vrf
+
+ lease_dir = directories['isc_dhclient_dir']
+ lease_files = []
+ lease_data = []
+
+ if interface:
+ tmp = f'{lease_dir}/dhclient_{interface}.lease'
+ if os.path.exists(tmp):
+ lease_files.append(tmp)
+ else:
+ # All DHCP leases
+ lease_files = glob(f'{lease_dir}/dhclient_*.lease')
+
+ for lease in lease_files:
+ tmp = {}
+ with open(lease, 'r') as f:
+ for line in f.readlines():
+ line = line.rstrip()
+ if 'last_update' not in tmp:
+ # ISC dhcp client contains least_update timestamp in human readable
+ # format this makes less sense for an API and also the expiry
+ # timestamp is provided in UNIX time. Convert string (e.g. Sun Jul
+ # 30 18:13:44 CEST 2023) to UNIX time (1690733624)
+ tmp.update({'last_update' : int(mktime(datetime.strptime(line, time_string).timetuple()))})
+ continue
+
+ k, v = line.split('=')
+ tmp.update({k : v.replace("'", "")})
+
+ if 'interface' in tmp:
+ vrf = get_interface_vrf(tmp['interface'])
+ if vrf: tmp.update({'vrf' : vrf})
+
+ lease_data.append(tmp)
+
+ return lease_data
+
+def _get_formatted_client_leases(lease_data, family):
+ from time import localtime
+ from time import strftime
+
+ from vyos.utils.network import is_intf_addr_assigned
+
+ data_entries = []
+ for lease in lease_data:
+ if not lease.get('new_ip_address'):
+ continue
+ data_entries.append(["Interface", lease['interface']])
+ if 'new_ip_address' in lease:
+ tmp = '[Active]' if is_intf_addr_assigned(lease['interface'], lease['new_ip_address']) else '[Inactive]'
+ data_entries.append(["IP address", lease['new_ip_address'], tmp])
+ if 'new_subnet_mask' in lease:
+ data_entries.append(["Subnet Mask", lease['new_subnet_mask']])
+ if 'new_domain_name' in lease:
+ data_entries.append(["Domain Name", lease['new_domain_name']])
+ if 'new_routers' in lease:
+ data_entries.append(["Router", lease['new_routers']])
+ if 'new_domain_name_servers' in lease:
+ data_entries.append(["Name Server", lease['new_domain_name_servers']])
+ if 'new_dhcp_server_identifier' in lease:
+ data_entries.append(["DHCP Server", lease['new_dhcp_server_identifier']])
+ if 'new_dhcp_lease_time' in lease:
+ data_entries.append(["DHCP Server", lease['new_dhcp_lease_time']])
+ if 'vrf' in lease:
+ data_entries.append(["VRF", lease['vrf']])
+ if 'last_update' in lease:
+ tmp = strftime(time_string, localtime(int(lease['last_update'])))
+ data_entries.append(["Last Update", tmp])
+ if 'new_expiry' in lease:
+ tmp = strftime(time_string, localtime(int(lease['new_expiry'])))
+ data_entries.append(["Expiry", tmp])
+
+ # Add empty marker
+ data_entries.append([''])
+
+ output = tabulate(data_entries, tablefmt='plain')
+
+ return output
+
+def show_client_leases(raw: bool, family: ArgFamily, interface: typing.Optional[str]):
+ lease_data = _get_raw_client_leases(family=family, interface=interface)
+ if raw:
+ return lease_data
+ else:
+ return _get_formatted_client_leases(lease_data, family=family)
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/op_mode/dns.py b/src/op_mode/dns.py
index f8863c530..2168aef89 100755
--- a/src/op_mode/dns.py
+++ b/src/op_mode/dns.py
@@ -20,7 +20,7 @@ import sys
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
+from vyos.utils.process import cmd
import vyos.opmode
diff --git a/src/op_mode/dynamic_dns.py b/src/op_mode/dns_dynamic.py
index d41a74db3..12aa5494a 100755
--- a/src/op_mode/dynamic_dns.py
+++ b/src/op_mode/dns_dynamic.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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,7 +22,7 @@ from tabulate import tabulate
from vyos.config import Config
from vyos.template import is_ipv4, is_ipv6
-from vyos.util import call
+from vyos.utils.process import call
cache_file = r'/run/ddclient/ddclient.cache'
diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py
index bfc640a26..55e20918f 100755
--- a/src/op_mode/dns_forwarding_reset.py
+++ b/src/op_mode/dns_forwarding_reset.py
@@ -25,7 +25,7 @@ import argparse
from sys import exit
from vyos.config import Config
-from vyos.util import call
+from vyos.utils.process import call
PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'
diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py
index d79b6c024..32b5c76a7 100755
--- a/src/op_mode/dns_forwarding_statistics.py
+++ b/src/op_mode/dns_forwarding_statistics.py
@@ -4,7 +4,7 @@ import jinja2
from sys import exit
from vyos.config import Config
-from vyos.util import cmd
+from vyos.utils.process import cmd
PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 46bda5f7e..581710b31 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -21,65 +21,31 @@ import re
import tabulate
from vyos.config import Config
-from vyos.util import cmd
-from vyos.util import dict_search_args
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search_args
-def get_firewall_interfaces(firewall, name=None, ipv6=False):
- directions = ['in', 'out', 'local']
-
- if 'interface' in firewall:
- for ifname, if_conf in firewall['interface'].items():
- for direction in directions:
- if direction not in if_conf:
- continue
-
- fw_conf = if_conf[direction]
- name_str = f'({ifname},{direction})'
-
- if 'name' in fw_conf:
- fw_name = fw_conf['name']
-
- if not name:
- firewall['name'][fw_name]['interface'].append(name_str)
- elif not ipv6 and name == fw_name:
- firewall['interface'].append(name_str)
-
- if 'ipv6_name' in fw_conf:
- fw_name = fw_conf['ipv6_name']
-
- if not name:
- firewall['ipv6_name'][fw_name]['interface'].append(name_str)
- elif ipv6 and name == fw_name:
- firewall['interface'].append(name_str)
-
- return firewall
-
-def get_config_firewall(conf, name=None, ipv6=False, interfaces=True):
+def get_config_firewall(conf, hook=None, priority=None, ipv6=False):
config_path = ['firewall']
- if name:
- config_path += ['ipv6-name' if ipv6 else 'name', name]
+ if hook:
+ config_path += ['ipv6' if ipv6 else 'ipv4', hook]
+ if priority:
+ config_path += [priority]
firewall = conf.get_config_dict(config_path, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
- if firewall and interfaces:
- if name:
- firewall['interface'] = {}
- else:
- if 'name' in firewall:
- for fw_name, name_conf in firewall['name'].items():
- name_conf['interface'] = []
- if 'ipv6_name' in firewall:
- for fw_name, name_conf in firewall['ipv6_name'].items():
- name_conf['interface'] = []
-
- get_firewall_interfaces(firewall, name, ipv6)
return firewall
-def get_nftables_details(name, ipv6=False):
+def get_nftables_details(hook, priority, ipv6=False):
suffix = '6' if ipv6 else ''
+ aux = 'IPV6_' if ipv6 else ''
name_prefix = 'NAME6_' if ipv6 else 'NAME_'
- command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{name}'
+ if hook == 'name' or hook == 'ipv6-name':
+ command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{priority}'
+ else:
+ up_hook = hook.upper()
+ command = f'sudo nft list chain ip{suffix} vyos_filter VYOS_{aux}{up_hook}_{priority}'
+
try:
results = cmd(command)
except:
@@ -87,7 +53,7 @@ def get_nftables_details(name, ipv6=False):
out = {}
for line in results.split('\n'):
- comment_search = re.search(rf'{name}[\- ](\d+|default-action)', line)
+ comment_search = re.search(rf'{priority}[\- ](\d+|default-action)', line)
if not comment_search:
continue
@@ -102,18 +68,15 @@ def get_nftables_details(name, ipv6=False):
out[rule_id] = rule
return out
-def output_firewall_name(name, name_conf, ipv6=False, single_rule_id=None):
+def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_id=None):
ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{name}"\n')
-
- if name_conf['interface']:
- print('Active on: {0}\n'.format(" ".join(name_conf['interface'])))
+ print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {priority}"\n')
- details = get_nftables_details(name, ipv6)
+ details = get_nftables_details(hook, priority, ipv6)
rows = []
- if 'rule' in name_conf:
- for rule_id, rule_conf in name_conf['rule'].items():
+ if 'rule' in firewall_conf:
+ for rule_id, rule_conf in firewall_conf['rule'].items():
if single_rule_id and rule_id != single_rule_id:
continue
@@ -128,8 +91,8 @@ def output_firewall_name(name, name_conf, ipv6=False, single_rule_id=None):
row.append(rule_details['conditions'])
rows.append(row)
- if 'default_action' in name_conf and not single_rule_id:
- row = ['default', name_conf['default_action'], 'all']
+ if 'default_action' in firewall_conf and not single_rule_id:
+ row = ['default', firewall_conf['default_action'], 'all']
if 'default-action' in details:
rule_details = details['default-action']
row.append(rule_details.get('packets', 0))
@@ -140,26 +103,56 @@ def output_firewall_name(name, name_conf, ipv6=False, single_rule_id=None):
header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
print(tabulate.tabulate(rows, header) + '\n')
-def output_firewall_name_statistics(name, name_conf, ipv6=False, single_rule_id=None):
+def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_rule_id=None):
ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{name}"\n')
+ print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {prior}"\n')
- if name_conf['interface']:
- print('Active on: {0}\n'.format(" ".join(name_conf['interface'])))
-
- details = get_nftables_details(name, ipv6)
+ details = get_nftables_details(hook, prior, ipv6)
rows = []
- if 'rule' in name_conf:
- for rule_id, rule_conf in name_conf['rule'].items():
+ if 'rule' in prior_conf:
+ for rule_id, rule_conf in prior_conf['rule'].items():
if single_rule_id and rule_id != single_rule_id:
continue
if 'disable' in rule_conf:
continue
- source_addr = dict_search_args(rule_conf, 'source', 'address') or '0.0.0.0/0'
- dest_addr = dict_search_args(rule_conf, 'destination', 'address') or '0.0.0.0/0'
+ # Get source
+ source_addr = dict_search_args(rule_conf, 'source', 'address')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'group', 'address_group')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'group', 'network_group')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'group', 'domain_group')
+ if not source_addr:
+ source_addr = '::/0' if ipv6 else '0.0.0.0/0'
+
+ # Get destination
+ dest_addr = dict_search_args(rule_conf, 'destination', 'address')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'address_group')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'network_group')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'domain_group')
+ if not dest_addr:
+ dest_addr = '::/0' if ipv6 else '0.0.0.0/0'
+
+ # Get inbound interface
+ iiface = dict_search_args(rule_conf, 'inbound_interface', 'interface_name')
+ if not iiface:
+ iiface = dict_search_args(rule_conf, 'inbound_interface', 'interface_group')
+ if not iiface:
+ iiface = 'any'
+
+ # Get outbound interface
+ oiface = dict_search_args(rule_conf, 'outbound_interface', 'interface_name')
+ if not oiface:
+ oiface = dict_search_args(rule_conf, 'outbound_interface', 'interface_group')
+ if not oiface:
+ oiface = 'any'
row = [rule_id]
if rule_id in details:
@@ -172,9 +165,11 @@ def output_firewall_name_statistics(name, name_conf, ipv6=False, single_rule_id=
row.append(rule_conf['action'])
row.append(source_addr)
row.append(dest_addr)
+ row.append(iiface)
+ row.append(oiface)
rows.append(row)
- if 'default_action' in name_conf and not single_rule_id:
+ if 'default_action' in prior_conf and not single_rule_id:
row = ['default']
if 'default-action' in details:
rule_details = details['default-action']
@@ -183,13 +178,13 @@ def output_firewall_name_statistics(name, name_conf, ipv6=False, single_rule_id=
else:
row.append('0')
row.append('0')
- row.append(name_conf['default_action'])
+ row.append(prior_conf['default_action'])
row.append('0.0.0.0/0') # Source
row.append('0.0.0.0/0') # Dest
rows.append(row)
if rows:
- header = ['Rule', 'Packets', 'Bytes', 'Action', 'Source', 'Destination']
+ header = ['Rule', 'Packets', 'Bytes', 'Action', 'Source', 'Destination', 'Inbound-Interface', 'Outbound-interface']
print(tabulate.tabulate(rows, header) + '\n')
def show_firewall():
@@ -201,52 +196,102 @@ def show_firewall():
if not firewall:
return
- if 'name' in firewall:
- for name, name_conf in firewall['name'].items():
- output_firewall_name(name, name_conf, ipv6=False)
+ if 'ipv4' in firewall:
+ for hook, hook_conf in firewall['ipv4'].items():
+ for prior, prior_conf in firewall['ipv4'][hook].items():
+ output_firewall_name(hook, prior, prior_conf, ipv6=False)
+
+ if 'ipv6' in firewall:
+ for hook, hook_conf in firewall['ipv6'].items():
+ for prior, prior_conf in firewall['ipv6'][hook].items():
+ output_firewall_name(hook, prior, prior_conf, ipv6=True)
- if 'ipv6_name' in firewall:
- for name, name_conf in firewall['ipv6_name'].items():
- output_firewall_name(name, name_conf, ipv6=True)
+def show_firewall_family(family):
+ print(f'Rulesets {family} Information')
-def show_firewall_name(name, ipv6=False):
+ conf = Config()
+ firewall = get_config_firewall(conf)
+
+ if not firewall:
+ return
+
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ if family == 'ipv6':
+ output_firewall_name(hook, prior, prior_conf, ipv6=True)
+ else:
+ output_firewall_name(hook, prior, prior_conf, ipv6=False)
+
+def show_firewall_name(hook, priority, ipv6=False):
print('Ruleset Information')
conf = Config()
- firewall = get_config_firewall(conf, name, ipv6)
+ firewall = get_config_firewall(conf, hook, priority, ipv6)
if firewall:
- output_firewall_name(name, firewall, ipv6)
+ output_firewall_name(hook, priority, firewall, ipv6)
-def show_firewall_rule(name, rule_id, ipv6=False):
+def show_firewall_rule(hook, priority, rule_id, ipv6=False):
print('Rule Information')
conf = Config()
- firewall = get_config_firewall(conf, name, ipv6)
+ firewall = get_config_firewall(conf, hook, priority, ipv6)
if firewall:
- output_firewall_name(name, firewall, ipv6, rule_id)
+ output_firewall_name(hook, priority, firewall, ipv6, rule_id)
def show_firewall_group(name=None):
conf = Config()
- firewall = get_config_firewall(conf, interfaces=False)
+ firewall = get_config_firewall(conf)
if 'group' not in firewall:
return
def find_references(group_type, group_name):
out = []
- for name_type in ['name', 'ipv6_name']:
- if name_type not in firewall:
- continue
- for name, name_conf in firewall[name_type].items():
- if 'rule' not in name_conf:
- continue
- for rule_id, rule_conf in name_conf['rule'].items():
- source_group = dict_search_args(rule_conf, 'source', 'group', group_type)
- dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type)
- if source_group and group_name == source_group:
- out.append(f'{name}-{rule_id}')
- elif dest_group and group_name == dest_group:
- out.append(f'{name}-{rule_id}')
+ family = []
+ if group_type in ['address_group', 'network_group']:
+ family = ['ipv4']
+ elif group_type == 'ipv6_address_group':
+ family = ['ipv6']
+ group_type = 'address_group'
+ elif group_type == 'ipv6_network_group':
+ family = ['ipv6']
+ group_type = 'network_group'
+ else:
+ family = ['ipv4', 'ipv6']
+
+ for item in family:
+ for name_type in ['name', 'ipv6_name', 'forward', 'input', 'output']:
+ if item in firewall:
+ if name_type not in firewall[item]:
+ continue
+ for priority, priority_conf in firewall[item][name_type].items():
+ if priority not in firewall[item][name_type]:
+ continue
+ for rule_id, rule_conf in priority_conf['rule'].items():
+ source_group = dict_search_args(rule_conf, 'source', 'group', group_type)
+ dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type)
+ in_interface = dict_search_args(rule_conf, 'inbound_interface', 'interface_group')
+ out_interface = dict_search_args(rule_conf, 'outbound_interface', 'interface_group')
+ if source_group:
+ if source_group[0] == "!":
+ source_group = source_group[1:]
+ if group_name == source_group:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
+ if dest_group:
+ if dest_group[0] == "!":
+ dest_group = dest_group[1:]
+ if group_name == dest_group:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
+ if in_interface:
+ if in_interface[0] == "!":
+ in_interface = in_interface[1:]
+ if group_name == in_interface:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
+ if out_interface:
+ if out_interface[0] == "!":
+ out_interface = out_interface[1:]
+ if group_name == out_interface:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
return out
header = ['Name', 'Type', 'References', 'Members']
@@ -267,6 +312,8 @@ def show_firewall_group(name=None):
row.append("\n".join(sorted(group_conf['mac_address'])))
elif 'port' in group_conf:
row.append("\n".join(sorted(group_conf['port'])))
+ elif 'interface' in group_conf:
+ row.append("\n".join(sorted(group_conf['interface'])))
else:
row.append('N/A')
rows.append(row)
@@ -284,28 +331,28 @@ def show_summary():
if not firewall:
return
- header = ['Ruleset Name', 'Description', 'References']
+ header = ['Ruleset Hook', 'Ruleset Priority', 'Description', 'References']
v4_out = []
v6_out = []
- if 'name' in firewall:
- for name, name_conf in firewall['name'].items():
- description = name_conf.get('description', '')
- interfaces = ", ".join(name_conf['interface'])
- v4_out.append([name, description, interfaces])
+ if 'ipv4' in firewall:
+ for hook, hook_conf in firewall['ipv4'].items():
+ for prior, prior_conf in firewall['ipv4'][hook].items():
+ description = prior_conf.get('description', '')
+ v4_out.append([hook, prior, description])
- if 'ipv6_name' in firewall:
- 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 or 'N/A'])
+ if 'ipv6' in firewall:
+ for hook, hook_conf in firewall['ipv6'].items():
+ for prior, prior_conf in firewall['ipv6'][hook].items():
+ description = prior_conf.get('description', '')
+ v6_out.append([hook, prior, description])
if v6_out:
- print('\nIPv6 name:\n')
+ print('\nIPv6 Ruleset:\n')
print(tabulate.tabulate(v6_out, header) + '\n')
if v4_out:
- print('\nIPv4 name:\n')
+ print('\nIPv4 Ruleset:\n')
print(tabulate.tabulate(v4_out, header) + '\n')
show_firewall_group()
@@ -319,18 +366,23 @@ def show_statistics():
if not firewall:
return
- if 'name' in firewall:
- for name, name_conf in firewall['name'].items():
- output_firewall_name_statistics(name, name_conf, ipv6=False)
+ if 'ipv4' in firewall:
+ for hook, hook_conf in firewall['ipv4'].items():
+ for prior, prior_conf in firewall['ipv4'][hook].items():
+ output_firewall_name_statistics(hook,prior, prior_conf, ipv6=False)
- if 'ipv6_name' in firewall:
- for name, name_conf in firewall['ipv6_name'].items():
- output_firewall_name_statistics(name, name_conf, ipv6=True)
+ if 'ipv6' in firewall:
+ for hook, hook_conf in firewall['ipv6'].items():
+ for prior, prior_conf in firewall['ipv6'][hook].items():
+ output_firewall_name_statistics(hook,prior, prior_conf, ipv6=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--action', help='Action', required=False)
parser.add_argument('--name', help='Firewall name', required=False, action='store', nargs='?', default='')
+ parser.add_argument('--family', help='IP family', required=False, action='store', nargs='?', default='')
+ parser.add_argument('--hook', help='Firewall hook', required=False, action='store', nargs='?', default='')
+ parser.add_argument('--priority', help='Firewall priority', required=False, action='store', nargs='?', default='')
parser.add_argument('--rule', help='Firewall Rule ID', required=False)
parser.add_argument('--ipv6', help='IPv6 toggle', action='store_true')
@@ -338,11 +390,13 @@ if __name__ == '__main__':
if args.action == 'show':
if not args.rule:
- show_firewall_name(args.name, args.ipv6)
+ show_firewall_name(args.hook, args.priority, args.ipv6)
else:
- show_firewall_rule(args.name, args.rule, args.ipv6)
+ show_firewall_rule(args.hook, args.priority, args.rule, args.ipv6)
elif args.action == 'show_all':
show_firewall()
+ elif args.action == 'show_family':
+ show_firewall_family(args.family)
elif args.action == 'show_group':
show_firewall_group(args.name)
elif args.action == 'show_statistics':
diff --git a/src/op_mode/flow_accounting_op.py b/src/op_mode/flow_accounting_op.py
index 514143cd7..497ccafdf 100755
--- a/src/op_mode/flow_accounting_op.py
+++ b/src/op_mode/flow_accounting_op.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,18 +13,18 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
import sys
import argparse
import re
import ipaddress
import os.path
+
from tabulate import tabulate
from json import loads
-from vyos.util import cmd
-from vyos.util import commit_in_progress
-from vyos.util import run
+from vyos.utils.commit import commit_in_progress
+from vyos.utils.process import cmd
+from vyos.utils.process import run
from vyos.logger import syslog
# some default values
diff --git a/src/op_mode/format_disk.py b/src/op_mode/format_disk.py
index b3ba44e87..31ceb196a 100755
--- a/src/op_mode/format_disk.py
+++ b/src/op_mode/format_disk.py
@@ -20,10 +20,10 @@ import re
from datetime import datetime
-from vyos.util import ask_yes_no
-from vyos.util import call
-from vyos.util import cmd
-from vyos.util import DEVNULL
+from vyos.utils.io import ask_yes_no
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.process import DEVNULL
def list_disks():
disks = set()
diff --git a/src/op_mode/generate_interfaces_debug_archive.py b/src/op_mode/generate_interfaces_debug_archive.py
index f5767080a..3059aad23 100755
--- a/src/op_mode/generate_interfaces_debug_archive.py
+++ b/src/op_mode/generate_interfaces_debug_archive.py
@@ -20,7 +20,7 @@ from shutil import rmtree
from socket import gethostname
from sys import exit
from tarfile import open as tar_open
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
import os
# define a list of commands that needs to be executed
diff --git a/src/op_mode/generate_ipsec_debug_archive.py b/src/op_mode/generate_ipsec_debug_archive.py
index 1422559a8..60195d48b 100755
--- a/src/op_mode/generate_ipsec_debug_archive.py
+++ b/src/op_mode/generate_ipsec_debug_archive.py
@@ -20,7 +20,7 @@ from shutil import rmtree
from socket import gethostname
from sys import exit
from tarfile import open as tar_open
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
# define a list of commands that needs to be executed
CMD_LIST: list[str] = [
diff --git a/src/op_mode/generate_openconnect_otp_key.py b/src/op_mode/generate_openconnect_otp_key.py
index 363bcf3ea..99b67d261 100755
--- a/src/op_mode/generate_openconnect_otp_key.py
+++ b/src/op_mode/generate_openconnect_otp_key.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,7 +17,7 @@
import argparse
import os
-from vyos.util import popen
+from vyos.utils.process import popen
from secrets import token_hex
from base64 import b32encode
diff --git a/src/op_mode/generate_ovpn_client_file.py b/src/op_mode/generate_ovpn_client_file.py
index 0628e6135..cec370a07 100755
--- a/src/op_mode/generate_ovpn_client_file.py
+++ b/src/op_mode/generate_ovpn_client_file.py
@@ -22,7 +22,7 @@ from textwrap import fill
from vyos.configquery import ConfigTreeQuery
from vyos.ifconfig import Section
-from vyos.util import cmd
+from vyos.utils.process import cmd
client_config = """
diff --git a/src/op_mode/generate_ssh_server_key.py b/src/op_mode/generate_ssh_server_key.py
index 43e94048d..d6063c43c 100755
--- a/src/op_mode/generate_ssh_server_key.py
+++ b/src/op_mode/generate_ssh_server_key.py
@@ -15,9 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sys import exit
-from vyos.util import ask_yes_no
-from vyos.util import cmd
-from vyos.util import commit_in_progress
+from vyos.utils.io import ask_yes_no
+from vyos.utils.process import cmd
+from vyos.utils.commit import commit_in_progress
if not ask_yes_no('Do you really want to remove the existing SSH host keys?'):
exit(0)
diff --git a/src/op_mode/generate_system_login_user.py b/src/op_mode/generate_system_login_user.py
index 8f8827b1b..1b328eae0 100755
--- a/src/op_mode/generate_system_login_user.py
+++ b/src/op_mode/generate_system_login_user.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,7 +17,7 @@
import argparse
import os
-from vyos.util import popen
+from vyos.utils.process import popen
from secrets import token_hex
from base64 import b32encode
diff --git a/src/op_mode/igmp-proxy.py b/src/op_mode/igmp-proxy.py
index 0086c9aa6..709e25915 100755
--- a/src/op_mode/igmp-proxy.py
+++ b/src/op_mode/igmp-proxy.py
@@ -28,16 +28,14 @@ import tabulate
import vyos.config
import vyos.opmode
-from vyos.util import bytes_to_human, print_error
+from vyos.utils.convert import bytes_to_human
+from vyos.utils.io import print_error
+from vyos.utils.process import process_named_running
def _is_configured():
"""Check if IGMP proxy is configured"""
return vyos.config.Config().exists_effective('protocols igmp-proxy')
-def _is_running():
- """Check if IGMP proxy is currently running"""
- return not vyos.util.run('ps -C igmpproxy')
-
def _kernel_to_ip(addr):
"""
Convert any given address from Linux kernel to a proper, IPv4 address
@@ -84,7 +82,7 @@ def show_interface(raw: bool):
if not _is_configured():
print_error('IGMP proxy is not configured.')
sys.exit(0)
-if not _is_running():
+if not process_named_running('igmpproxy'):
print_error('IGMP proxy is not running.')
sys.exit(0)
diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py
index a22f04c45..5454cc0ce 100755
--- a/src/op_mode/ikev2_profile_generator.py
+++ b/src/op_mode/ikev2_profile_generator.py
@@ -24,7 +24,7 @@ from cryptography.x509.oid import NameOID
from vyos.configquery import ConfigTreeQuery
from vyos.pki import load_certificate
from vyos.template import render_to_string
-from vyos.util import ask_input
+from vyos.utils.io import ask_input
# Apple profiles only support one IKE/ESP encryption cipher and hash, whereas
# VyOS comes with a multitude of different proposals for a connection.
diff --git a/src/op_mode/interfaces.py b/src/op_mode/interfaces.py
index f38b95a71..782e178c6 100755
--- a/src/op_mode/interfaces.py
+++ b/src/op_mode/interfaces.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,7 +28,9 @@ import vyos.opmode
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
from vyos.ifconfig import VRRP
-from vyos.util import cmd, rc_cmd, call
+from vyos.utils.process import cmd
+from vyos.utils.process import rc_cmd
+from vyos.utils.process import call
def catch_broken_pipe(func):
def wrapped(*args, **kwargs):
@@ -384,7 +386,7 @@ def _format_show_counters(data: list):
rx_errors = entry.get('rx_over_errors')
tx_errors = entry.get('tx_carrier_errors')
data_entries.append([interface, rx_packets, rx_bytes, tx_packets, tx_bytes, rx_dropped, tx_dropped, rx_errors, tx_errors])
-
+
headers = ['Interface', 'Rx Packets', 'Rx Bytes', 'Tx Packets', 'Tx Bytes', 'Rx Dropped', 'Tx Dropped', 'Rx Errors', 'Tx Errors']
output = tabulate(data_entries, headers, numalign="left")
print (output)
diff --git a/src/op_mode/ipoe-control.py b/src/op_mode/ipoe-control.py
index 7111498b2..0f33beca7 100755
--- a/src/op_mode/ipoe-control.py
+++ b/src/op_mode/ipoe-control.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,7 +18,8 @@ import sys
import argparse
from vyos.config import Config
-from vyos.util import popen, run
+from vyos.utils.process import popen
+from vyos.utils.process import run
cmd_dict = {
'cmd_base' : '/usr/bin/accel-cmd -p 2002 ',
diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py
index 823bd039d..57d3cfed9 100755
--- a/src/op_mode/ipsec.py
+++ b/src/op_mode/ipsec.py
@@ -21,9 +21,9 @@ from hurry import filesize
from re import split as re_split
from tabulate import tabulate
-from vyos.util import convert_data
-from vyos.util import seconds_to_human
-from vyos.util import cmd
+from vyos.utils.convert import convert_data
+from vyos.utils.convert import seconds_to_human
+from vyos.utils.process import cmd
from vyos.configquery import ConfigTreeQuery
import vyos.opmode
diff --git a/src/op_mode/lldp.py b/src/op_mode/lldp.py
index 1a1b94783..c287b8fa6 100755
--- a/src/op_mode/lldp.py
+++ b/src/op_mode/lldp.py
@@ -22,8 +22,8 @@ import typing
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
-from vyos.util import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
import vyos.opmode
unconf_message = 'LLDP is not configured'
diff --git a/src/op_mode/log.py b/src/op_mode/log.py
index b0abd6191..797ba5a88 100755
--- a/src/op_mode/log.py
+++ b/src/op_mode/log.py
@@ -21,7 +21,7 @@ import typing
from jinja2 import Template
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
import vyos.opmode
diff --git a/src/op_mode/memory.py b/src/op_mode/memory.py
index 7666de646..eb530035b 100755
--- a/src/op_mode/memory.py
+++ b/src/op_mode/memory.py
@@ -54,7 +54,7 @@ def _get_raw_data():
return mem_data
def _get_formatted_output(mem):
- from vyos.util import bytes_to_human
+ from vyos.utils.convert import bytes_to_human
# For human-readable outputs, we convert bytes to more convenient units
# (100M, 1.3G...)
diff --git a/src/op_mode/nat.py b/src/op_mode/nat.py
index c92795745..71a40c0e1 100755
--- a/src/op_mode/nat.py
+++ b/src/op_mode/nat.py
@@ -25,8 +25,8 @@ from tabulate import tabulate
import vyos.opmode
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
-from vyos.util import dict_search
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
base = 'nat'
unconf_message = 'NAT is not configured'
diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py
index b329ea280..8b3c45c7c 100755
--- a/src/op_mode/neighbor.py
+++ b/src/op_mode/neighbor.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,17 +31,14 @@ import sys
import typing
import vyos.opmode
+from vyos.utils.network import interface_exists
ArgFamily = typing.Literal['inet', 'inet6']
ArgState = typing.Literal['reachable', 'stale', 'failed', 'permanent']
-def interface_exists(interface):
- import os
- return os.path.exists(f'/sys/class/net/{interface}')
-
def get_raw_data(family, interface=None, state=None):
from json import loads
- from vyos.util import cmd
+ from vyos.utils.process import cmd
if interface:
if not interface_exists(interface):
@@ -102,7 +99,7 @@ def show(raw: bool, family: ArgFamily, interface: typing.Optional[str],
return format_neighbors(data, interface)
def reset(family: ArgFamily, interface: typing.Optional[str], address: typing.Optional[str]):
- from vyos.util import run
+ from vyos.utils.process import run
if address and interface:
raise ValueError("interface and address parameters are mutually exclusive")
@@ -114,7 +111,6 @@ def reset(family: ArgFamily, interface: typing.Optional[str], address: typing.Op
# Flush an entire neighbor table
run(f"""ip --family {family} neighbor flush""")
-
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/op_mode/nhrp.py b/src/op_mode/nhrp.py
index 5ff91a59c..e66f33079 100755
--- a/src/op_mode/nhrp.py
+++ b/src/op_mode/nhrp.py
@@ -18,9 +18,9 @@ import sys
import tabulate
import vyos.opmode
-from vyos.util import cmd
-from vyos.util import process_named_running
-from vyos.util import colon_separated_to_dict
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.dict import colon_separated_to_dict
def _get_formatted_output(output_dict: dict) -> str:
diff --git a/src/op_mode/openconnect-control.py b/src/op_mode/openconnect-control.py
index 20c50e779..b70d4fa16 100755
--- a/src/op_mode/openconnect-control.py
+++ b/src/op_mode/openconnect-control.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,12 +18,13 @@ import sys
import argparse
import json
-from vyos.config import Config
-from vyos.util import popen
-from vyos.util import run
-from vyos.util import DEVNULL
from tabulate import tabulate
+from vyos.config import Config
+from vyos.utils.process import popen
+from vyos.utils.process import run
+from vyos.utils.process import DEVNULL
+
occtl = '/usr/bin/occtl'
occtl_socket = '/run/ocserv/occtl.socket'
diff --git a/src/op_mode/openconnect.py b/src/op_mode/openconnect.py
index b21890728..cfa0678a7 100755
--- a/src/op_mode/openconnect.py
+++ b/src/op_mode/openconnect.py
@@ -19,7 +19,7 @@ import json
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
import vyos.opmode
diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py
index d9ae965c5..fd9d2db92 100755
--- a/src/op_mode/openvpn.py
+++ b/src/op_mode/openvpn.py
@@ -23,10 +23,10 @@ import typing
from tabulate import tabulate
import vyos.opmode
-from vyos.util import bytes_to_human
-from vyos.util import commit_in_progress
-from vyos.util import call
-from vyos.util import rc_cmd
+from vyos.utils.convert import bytes_to_human
+from vyos.utils.commit import commit_in_progress
+from vyos.utils.process import call
+from vyos.utils.process import rc_cmd
from vyos.config import Config
ArgMode = typing.Literal['client', 'server', 'site_to_site']
diff --git a/src/op_mode/ping.py b/src/op_mode/ping.py
index 610e63cb3..f1d87a118 100755
--- a/src/op_mode/ping.py
+++ b/src/op_mode/ping.py
@@ -18,7 +18,7 @@ import os
import sys
import socket
import ipaddress
-from vyos.util import get_all_vrfs
+from vyos.utils.network import get_all_vrfs
from vyos.ifconfig import Section
@@ -90,6 +90,16 @@ options = {
'type': '<seconds>',
'help': 'Number of seconds to wait between requests'
},
+ 'ipv4': {
+ 'ping': '{command} -4',
+ 'type': 'noarg',
+ 'help': 'Use IPv4 only'
+ },
+ 'ipv6': {
+ 'ping': '{command} -6',
+ 'type': 'noarg',
+ 'help': 'Use IPv6 only'
+ },
'mark': {
'ping': '{command} -m {value}',
'type': '<fwmark>',
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index b054690b0..35c7ce0e2 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,33 +25,31 @@ from cryptography import x509
from cryptography.x509.oid import ExtendedKeyUsageOID
from vyos.config import Config
-from vyos.configquery import ConfigTreeQuery
-from vyos.configdict import dict_merge
from vyos.pki import encode_certificate, encode_public_key, encode_private_key, encode_dh_parameters
+from vyos.pki import get_certificate_fingerprint
from vyos.pki import create_certificate, create_certificate_request, create_certificate_revocation_list
from vyos.pki import create_private_key
from vyos.pki import create_dh_parameters
from vyos.pki import load_certificate, load_certificate_request, load_private_key
from vyos.pki import load_crl, load_dh_parameters, load_public_key
from vyos.pki import verify_certificate
-from vyos.xml import defaults
-from vyos.util import ask_input, ask_yes_no
-from vyos.util import cmd
-from vyos.util import install_into_config
+from vyos.utils.io import ask_input
+from vyos.utils.io import ask_yes_no
+from vyos.utils.misc import install_into_config
+from vyos.utils.process import cmd
CERT_REQ_END = '-----END CERTIFICATE REQUEST-----'
auth_dir = '/config/auth'
# Helper Functions
-conf = ConfigTreeQuery()
+conf = Config()
def get_default_values():
# Fetch default x509 values
base = ['pki', 'x509', 'default']
x509_defaults = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
get_first_key=True,
- no_tag_node_value_mangle=True)
- default_values = defaults(base)
- x509_defaults = dict_merge(default_values, x509_defaults)
+ with_recursive_defaults=True)
return x509_defaults
@@ -190,7 +188,7 @@ def install_ssh_key(name, public_key, private_key, passphrase=None):
def install_keypair(name, key_type, private_key=None, public_key=None, passphrase=None, prompt=True):
# Show/install conf commands for key-pair
-
+
config_paths = []
if public_key:
@@ -840,7 +838,7 @@ def import_openvpn_secret(name, path):
install_openvpn_key(name, key_data, key_version)
# Show functions
-def show_certificate_authority(name=None):
+def show_certificate_authority(name=None, pem=False):
headers = ['Name', 'Subject', 'Issuer CN', 'Issued', 'Expiry', 'Private Key', 'Parent']
data = []
certs = get_config_ca_certificate()
@@ -852,6 +850,11 @@ def show_certificate_authority(name=None):
continue
cert = load_certificate(cert_dict['certificate'])
+
+ if name and pem:
+ print(encode_certificate(cert))
+ return
+
parent_ca_name = get_certificate_ca(cert, certs)
cert_issuer_cn = cert.issuer.rfc4514_string().split(",")[0]
@@ -867,7 +870,7 @@ def show_certificate_authority(name=None):
print("Certificate Authorities:")
print(tabulate.tabulate(data, headers))
-def show_certificate(name=None):
+def show_certificate(name=None, pem=False):
headers = ['Name', 'Type', 'Subject CN', 'Issuer CN', 'Issued', 'Expiry', 'Revoked', 'Private Key', 'CA Present']
data = []
certs = get_config_certificate()
@@ -885,6 +888,10 @@ def show_certificate(name=None):
if not cert:
continue
+ if name and pem:
+ print(encode_certificate(cert))
+ return
+
ca_name = get_certificate_ca(cert, ca_certs)
cert_subject_cn = cert.subject.rfc4514_string().split(",")[0]
cert_issuer_cn = cert.issuer.rfc4514_string().split(",")[0]
@@ -906,7 +913,13 @@ def show_certificate(name=None):
print("Certificates:")
print(tabulate.tabulate(data, headers))
-def show_crl(name=None):
+def show_certificate_fingerprint(name, hash):
+ cert = get_config_certificate(name=name)
+ cert = load_certificate(cert['certificate'])
+
+ print(get_certificate_fingerprint(cert, hash))
+
+def show_crl(name=None, pem=False):
headers = ['CA Name', 'Updated', 'Revokes']
data = []
certs = get_config_ca_certificate()
@@ -927,9 +940,16 @@ def show_crl(name=None):
if not crl:
continue
+ if name and pem:
+ print(encode_certificate(crl))
+ continue
+
certs = get_revoked_by_serial_numbers([revoked.serial_number for revoked in crl])
data.append([cert_name, crl.last_update, ", ".join(certs)])
+ if name and pem:
+ return
+
print("Certificate Revocation Lists:")
print(tabulate.tabulate(data, headers))
@@ -943,6 +963,8 @@ if __name__ == '__main__':
parser.add_argument('--crl', help='Certificate Revocation List', required=False)
parser.add_argument('--sign', help='Sign certificate with specified CA', required=False)
parser.add_argument('--self-sign', help='Self-sign the certificate', action='store_true')
+ parser.add_argument('--pem', help='Output using PEM encoding', action='store_true')
+ parser.add_argument('--fingerprint', help='Show fingerprint and exit', action='store')
# SSH
parser.add_argument('--ssh', help='SSH Key', required=False)
@@ -1032,16 +1054,19 @@ if __name__ == '__main__':
if not conf.exists(['pki', 'ca', ca_name]):
print(f'CA "{ca_name}" does not exist!')
exit(1)
- show_certificate_authority(ca_name)
+ show_certificate_authority(ca_name, args.pem)
elif args.certificate:
cert_name = None if args.certificate == 'all' else args.certificate
if cert_name:
if not conf.exists(['pki', 'certificate', cert_name]):
print(f'Certificate "{cert_name}" does not exist!')
exit(1)
- show_certificate(None if args.certificate == 'all' else args.certificate)
+ if args.fingerprint is None:
+ show_certificate(None if args.certificate == 'all' else args.certificate, args.pem)
+ else:
+ show_certificate_fingerprint(args.certificate, args.fingerprint)
elif args.crl:
- show_crl(None if args.crl == 'all' else args.crl)
+ show_crl(None if args.crl == 'all' else args.crl, args.pem)
else:
show_certificate_authority()
show_certificate()
diff --git a/src/op_mode/policy_route.py b/src/op_mode/policy_route.py
index 5953786f3..eff99de7f 100755
--- a/src/op_mode/policy_route.py
+++ b/src/op_mode/policy_route.py
@@ -19,8 +19,8 @@ import re
import tabulate
from vyos.config import Config
-from vyos.util import cmd
-from vyos.util import dict_search_args
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search_args
def get_config_policy(conf, name=None, ipv6=False):
config_path = ['policy']
@@ -61,8 +61,10 @@ def output_policy_route(name, route_conf, ipv6=False, single_rule_id=None):
ip_str = 'IPv6' if ipv6 else 'IPv4'
print(f'\n---------------------------------\n{ip_str} Policy Route "{name}"\n')
- if route_conf['interface']:
+ if route_conf.get('interface'):
print('Active on: {0}\n'.format(" ".join(route_conf['interface'])))
+ else:
+ print('Inactive - Not applied to any interfaces\n')
details = get_nftables_details(name, ipv6)
rows = []
diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py
index fd4f86d88..3ac5991b4 100755
--- a/src/op_mode/powerctrl.py
+++ b/src/op_mode/powerctrl.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2023 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,7 +22,11 @@ from datetime import datetime, timedelta, time as type_time, date as type_date
from sys import exit
from time import time
-from vyos.util import ask_yes_no, cmd, call, run, STDOUT
+from vyos.utils.io import ask_yes_no
+from vyos.utils.process import call
+from vyos.utils.process import cmd
+from vyos.utils.process import run
+from vyos.utils.process import STDOUT
systemd_sched_file = "/run/systemd/shutdown/scheduled"
@@ -102,8 +106,19 @@ def cancel_shutdown():
else:
print("Reboot or poweroff is not scheduled")
+def check_unsaved_config():
+ from vyos.config_mgmt import unsaved_commits
+ from vyos.utils.boot import boot_configuration_success
+
+ if unsaved_commits() and boot_configuration_success():
+ print("Warning: there are unsaved configuration changes!")
+ print("Run 'save' command if you do not want to lose those changes after reboot/shutdown.")
+ else:
+ pass
def execute_shutdown(time, reboot=True, ask=True):
+ check_unsaved_config()
+
action = "reboot" if reboot else "poweroff"
if not ask:
if not ask_yes_no(f"Are you sure you want to {action} this system?"):
diff --git a/src/op_mode/ppp-server-ctrl.py b/src/op_mode/ppp-server-ctrl.py
index e93963fdd..2bae5b32a 100755
--- a/src/op_mode/ppp-server-ctrl.py
+++ b/src/op_mode/ppp-server-ctrl.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,7 +18,8 @@ import sys
import argparse
from vyos.config import Config
-from vyos.util import popen, DEVNULL
+from vyos.utils.process import popen
+from vyos.utils.process import DEVNULL
cmd_dict = {
'cmd_base' : '/usr/bin/accel-cmd -p {} ',
diff --git a/src/op_mode/reset_openvpn.py b/src/op_mode/reset_openvpn.py
index efbf65083..cef5299da 100755
--- a/src/op_mode/reset_openvpn.py
+++ b/src/op_mode/reset_openvpn.py
@@ -16,8 +16,8 @@
import os
from sys import argv, exit
-from vyos.util import call
-from vyos.util import commit_in_progress
+from vyos.utils.process import call
+from vyos.utils.commit import commit_in_progress
if __name__ == '__main__':
if (len(argv) < 1):
diff --git a/src/op_mode/reset_vpn.py b/src/op_mode/reset_vpn.py
index 46195d6cd..61d7c8c81 100755
--- a/src/op_mode/reset_vpn.py
+++ b/src/op_mode/reset_vpn.py
@@ -13,10 +13,11 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
import sys
import typing
-from vyos.util import run
+from vyos.utils.process import run
import vyos.opmode
@@ -29,7 +30,6 @@ cmd_dict = {
}
}
-
def reset_conn(protocol: str, username: typing.Optional[str] = None,
interface: typing.Optional[str] = None):
if protocol in cmd_dict['vpn_types']:
diff --git a/src/op_mode/restart_dhcp_relay.py b/src/op_mode/restart_dhcp_relay.py
index 9203c009f..3ead97f4c 100755
--- a/src/op_mode/restart_dhcp_relay.py
+++ b/src/op_mode/restart_dhcp_relay.py
@@ -23,8 +23,8 @@ import argparse
import os
import vyos.config
-from vyos.util import call
-from vyos.util import commit_in_progress
+from vyos.utils.process import call
+from vyos.utils.commit import commit_in_progress
parser = argparse.ArgumentParser()
diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py
index 680d9f8cc..5cce377eb 100755
--- a/src/op_mode/restart_frr.py
+++ b/src/op_mode/restart_frr.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,10 +23,10 @@ from logging.handlers import SysLogHandler
from shutil import rmtree
from vyos.base import Warning
-from vyos.util import call
-from vyos.util import ask_yes_no
-from vyos.util import process_named_running
-from vyos.util import makedir
+from vyos.utils.io import ask_yes_no
+from vyos.utils.file import makedir
+from vyos.utils.process import call
+from vyos.utils.process import process_named_running
# some default values
watchfrr = '/usr/lib/frr/watchfrr.sh'
diff --git a/src/op_mode/reverseproxy.py b/src/op_mode/reverseproxy.py
new file mode 100755
index 000000000..44ffd7a37
--- /dev/null
+++ b/src/op_mode/reverseproxy.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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 json
+import socket
+import sys
+import typing
+
+from sys import exit
+from tabulate import tabulate
+from vyos.configquery import ConfigTreeQuery
+
+import vyos.opmode
+
+socket_path = '/run/haproxy/admin.sock'
+timeout = 5
+
+
+def _execute_haproxy_command(command):
+ """Execute a command on the HAProxy UNIX socket and retrieve the response.
+
+ Args:
+ command (str): The command to be executed.
+
+ Returns:
+ str: The response received from the HAProxy UNIX socket.
+
+ Raises:
+ socket.error: If there is an error while connecting or communicating with the socket.
+
+ Finally:
+ Closes the socket connection.
+
+ Notes:
+ - HAProxy expects a newline character at the end of the command.
+ - The socket connection is established using the HAProxy UNIX socket.
+ - The response from the socket is received and decoded.
+
+ Example:
+ response = _execute_haproxy_command('show stat')
+ print(response)
+ """
+ try:
+ # HAProxy expects new line for command
+ command = f'{command}\n'
+
+ # Connect to the HAProxy UNIX socket
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(socket_path)
+
+ # Set the socket timeout
+ sock.settimeout(timeout)
+
+ # Send the command
+ sock.sendall(command.encode())
+
+ # Receive and decode the response
+ response = b''
+ while True:
+ data = sock.recv(4096)
+ if not data:
+ break
+ response += data
+ response = response.decode()
+
+ return (response)
+
+ except socket.error as e:
+ print(f"Error: {e}")
+
+ finally:
+ # Close the socket
+ sock.close()
+
+
+def _convert_seconds(seconds):
+ """Convert seconds to days, hours, minutes, and seconds.
+
+ Args:
+ seconds (int): The number of seconds to convert.
+
+ Returns:
+ tuple: A tuple containing the number of days, hours, minutes, and seconds.
+ """
+ minutes = seconds // 60
+ hours = minutes // 60
+ days = hours // 24
+
+ return days, hours % 24, minutes % 60, seconds % 60
+
+
+def _last_change_format(seconds):
+ """Format the time components into a string representation.
+
+ Args:
+ seconds (int): The total number of seconds.
+
+ Returns:
+ str: The formatted time string with days, hours, minutes, and seconds.
+
+ Examples:
+ >>> _last_change_format(1434)
+ '23m54s'
+ >>> _last_change_format(93734)
+ '1d0h23m54s'
+ >>> _last_change_format(85434)
+ '23h23m54s'
+ """
+ days, hours, minutes, seconds = _convert_seconds(seconds)
+ time_format = ""
+
+ if days:
+ time_format += f"{days}d"
+ if hours:
+ time_format += f"{hours}h"
+ if minutes:
+ time_format += f"{minutes}m"
+ if seconds:
+ time_format += f"{seconds}s"
+
+ return time_format
+
+
+def _get_json_data():
+ """Get haproxy data format JSON"""
+ return _execute_haproxy_command('show stat json')
+
+
+def _get_raw_data():
+ """Retrieve raw data from JSON and organize it into a dictionary.
+
+ Returns:
+ dict: A dictionary containing the organized data categorized
+ into frontend, backend, and server.
+ """
+
+ data = json.loads(_get_json_data())
+ lb_dict = {'frontend': [], 'backend': [], 'server': []}
+
+ for key in data:
+ frontend = []
+ backend = []
+ server = []
+ for entry in key:
+ obj_type = entry['objType'].lower()
+ position = entry['field']['pos']
+ name = entry['field']['name']
+ value = entry['value']['value']
+
+ dict_entry = {'pos': position, 'name': {name: value}}
+
+ if obj_type == 'frontend':
+ frontend.append(dict_entry)
+ elif obj_type == 'backend':
+ backend.append(dict_entry)
+ elif obj_type == 'server':
+ server.append(dict_entry)
+
+ if len(frontend) > 0:
+ lb_dict['frontend'].append(frontend)
+ if len(backend) > 0:
+ lb_dict['backend'].append(backend)
+ if len(server) > 0:
+ lb_dict['server'].append(server)
+
+ return lb_dict
+
+
+def _get_formatted_output(data):
+ """
+ Format the data into a tabulated output.
+
+ Args:
+ data (dict): The data to be formatted.
+
+ Returns:
+ str: The tabulated output representing the formatted data.
+ """
+ table = []
+ headers = [
+ "Proxy name", "Role", "Status", "Req rate", "Resp time", "Last change"
+ ]
+
+ for key in data:
+ for item in data[key]:
+ row = [None] * len(headers)
+
+ for element in item:
+ if 'pxname' in element['name']:
+ row[0] = element['name']['pxname']
+ elif 'svname' in element['name']:
+ row[1] = element['name']['svname']
+ elif 'status' in element['name']:
+ row[2] = element['name']['status']
+ elif 'req_rate' in element['name']:
+ row[3] = element['name']['req_rate']
+ elif 'rtime' in element['name']:
+ row[4] = f"{element['name']['rtime']} ms"
+ elif 'lastchg' in element['name']:
+ row[5] = _last_change_format(element['name']['lastchg'])
+ table.append(row)
+
+ out = tabulate(table, headers, numalign="left")
+ return out
+
+
+def show(raw: bool):
+ config = ConfigTreeQuery()
+ if not config.exists('load-balancing reverse-proxy'):
+ raise vyos.opmode.UnconfiguredSubsystem('Reverse-proxy is not configured')
+
+ data = _get_raw_data()
+ if raw:
+ return data
+ else:
+ return _get_formatted_output(data)
+
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except (ValueError, vyos.opmode.Error) as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/op_mode/route.py b/src/op_mode/route.py
index d6d6b7d6f..4aa57dbf4 100755
--- a/src/op_mode/route.py
+++ b/src/op_mode/route.py
@@ -57,7 +57,7 @@ frr_command_template = Template("""
ArgFamily = typing.Literal['inet', 'inet6']
def show_summary(raw: bool, family: ArgFamily, table: typing.Optional[int], vrf: typing.Optional[str]):
- from vyos.util import cmd
+ from vyos.utils.process import cmd
if family == 'inet':
family_cmd = 'ip'
@@ -119,7 +119,7 @@ def show(raw: bool,
frr_command = frr_command_template.render(kwargs)
frr_command = re.sub(r'\s+', ' ', frr_command)
- from vyos.util import cmd
+ from vyos.utils.process import cmd
output = cmd(f"vtysh -c '{frr_command}'")
if raw:
diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py
index 88f70d6bd..dca7f44cb 100755
--- a/src/op_mode/sflow.py
+++ b/src/op_mode/sflow.py
@@ -20,7 +20,7 @@ import sys
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
+from vyos.utils.process import cmd
import vyos.opmode
diff --git a/src/op_mode/show-bond.py b/src/op_mode/show-bond.py
index edf7847fc..f676e0841 100755
--- a/src/op_mode/show-bond.py
+++ b/src/op_mode/show-bond.py
@@ -19,7 +19,7 @@ import jinja2
from argparse import ArgumentParser
from vyos.ifconfig import Section
from vyos.ifconfig import BondIf
-from vyos.util import read_file
+from vyos.utils.file import read_file
from sys import exit
diff --git a/src/op_mode/show_acceleration.py b/src/op_mode/show_acceleration.py
index 48c31d4d9..1c4831f1d 100755
--- a/src/op_mode/show_acceleration.py
+++ b/src/op_mode/show_acceleration.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,8 +20,8 @@ import re
import argparse
from vyos.config import Config
-from vyos.util import popen
-from vyos.util import call
+from vyos.utils.process import call
+from vyos.utils.process import popen
def detect_qat_dev():
output, err = popen('lspci -nn', decode='utf-8')
diff --git a/src/op_mode/show_ntp.sh b/src/op_mode/show_ntp.sh
index 85f8eda15..4b59b801e 100755
--- a/src/op_mode/show_ntp.sh
+++ b/src/op_mode/show_ntp.sh
@@ -18,7 +18,7 @@ if ! ps -C chronyd &>/dev/null; then
fi
PID=$(pgrep chronyd | head -n1)
-VRF_NAME=$(ip vrf identify )
+VRF_NAME=$(ip vrf identify ${PID})
if [ ! -z ${VRF_NAME} ]; then
VRF_CMD="sudo ip vrf exec ${VRF_NAME}"
diff --git a/src/op_mode/show_openconnect_otp.py b/src/op_mode/show_openconnect_otp.py
index 88982c50b..3771fb385 100755
--- a/src/op_mode/show_openconnect_otp.py
+++ b/src/op_mode/show_openconnect_otp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2017, 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2017-2023 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
@@ -17,12 +17,11 @@
import argparse
import os
+from base64 import b32encode
from vyos.config import Config
-from vyos.xml import defaults
-from vyos.configdict import dict_merge
-from vyos.util import popen
-from base64 import b32encode
+from vyos.utils.dict import dict_search_args
+from vyos.utils.process import popen
otp_file = '/run/ocserv/users.oath'
@@ -33,7 +32,7 @@ def check_uname_otp(username):
config = Config()
base_key = ['vpn', 'openconnect', 'authentication', 'local-users', 'username', username, 'otp', 'key']
if not config.exists(base_key):
- return None
+ return False
return True
def get_otp_ocserv(username):
@@ -41,21 +40,21 @@ def get_otp_ocserv(username):
base = ['vpn', 'openconnect']
if not config.exists(base):
return None
- ocserv = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # 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)
- ocserv = dict_merge(default_values, ocserv)
- # workaround a "know limitation" - https://vyos.dev/T2665
- del ocserv['authentication']['local_users']['username']['otp']
- if not ocserv["authentication"]["local_users"]["username"]:
+
+ ocserv = config.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
+
+ user_path = ['authentication', 'local_users', 'username']
+ users = dict_search_args(ocserv, *user_path)
+
+ if users is None:
return None
- default_ocserv_usr_values = default_values['authentication']['local_users']['username']['otp']
- for user, params in ocserv['authentication']['local_users']['username'].items():
- # Not every configuration requires OTP settings
- if ocserv['authentication']['local_users']['username'][user].get('otp'):
- ocserv['authentication']['local_users']['username'][user]['otp'] = dict_merge(default_ocserv_usr_values, ocserv['authentication']['local_users']['username'][user]['otp'])
- result = ocserv['authentication']['local_users']['username'][username]
+
+ # function is called conditionally, if check_uname_otp true, so username
+ # exists
+ result = users[username]
+
return result
def display_otp_ocserv(username, params, info):
@@ -101,8 +100,7 @@ if __name__ == '__main__':
parser.add_argument('--info', action="store", type=str, default='full', help='Wich information to display')
args = parser.parse_args()
- check_otp = check_uname_otp(args.user)
- if check_otp:
+ if check_uname_otp(args.user):
user_otp_params = get_otp_ocserv(args.user)
display_otp_ocserv(args.user, user_otp_params, args.info)
else:
diff --git a/src/op_mode/show_openvpn_mfa.py b/src/op_mode/show_openvpn_mfa.py
index 1ab54600c..100c42154 100755
--- a/src/op_mode/show_openvpn_mfa.py
+++ b/src/op_mode/show_openvpn_mfa.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-
-# Copyright 2017, 2021 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# Copyright 2017-2023 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
@@ -20,7 +20,7 @@ import socket
import urllib.parse
import argparse
-from vyos.util import popen
+from vyos.utils.process import popen
otp_file = '/config/auth/openvpn/{interface}-otp-secrets'
diff --git a/src/op_mode/show_sensors.py b/src/op_mode/show_sensors.py
index 6ae477647..5e3084fe9 100755
--- a/src/op_mode/show_sensors.py
+++ b/src/op_mode/show_sensors.py
@@ -1,9 +1,25 @@
#!/usr/bin/env python3
+#
+# Copyright 2017-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import re
import sys
-from vyos.util import popen
-from vyos.util import DEVNULL
+from vyos.utils.process import popen
+from vyos.utils.process import DEVNULL
+
output,retcode = popen("sensors --no-adapter", stderr=DEVNULL)
if retcode == 0:
print (output)
@@ -23,5 +39,3 @@ else:
print ("No sensors found")
sys.exit(1)
-
-
diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py
index 782004144..53144fd52 100644
--- a/src/op_mode/show_techsupport_report.py
+++ b/src/op_mode/show_techsupport_report.py
@@ -17,7 +17,7 @@
import os
from typing import List
-from vyos.util import rc_cmd
+from vyos.utils.process import rc_cmd
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
@@ -91,7 +91,8 @@ def show_package_repository_config() -> None:
def show_user_startup_scripts() -> None:
"""Prints the user startup scripts."""
- execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts')
+ execute_command('cat /config/scripts/vyos-preconfig-bootup.script', 'User Startup Scripts (Preconfig)')
+ execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts (Postconfig)')
def show_frr_config() -> None:
diff --git a/src/op_mode/show_virtual_server.py b/src/op_mode/show_virtual_server.py
index 377180dec..7880edc97 100755
--- a/src/op_mode/show_virtual_server.py
+++ b/src/op_mode/show_virtual_server.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from vyos.configquery import ConfigTreeQuery
-from vyos.util import call
+from vyos.utils.process import call
def is_configured():
""" Check if high-availability virtual-server is configured """
diff --git a/src/op_mode/show_wireless.py b/src/op_mode/show_wireless.py
index 19ab6771c..340163057 100755
--- a/src/op_mode/show_wireless.py
+++ b/src/op_mode/show_wireless.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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,7 +21,7 @@ from sys import exit
from copy import deepcopy
from vyos.config import Config
-from vyos.util import popen
+from vyos.utils.process import popen
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--scan", help="Scan for Wireless APs on given interface, e.g. 'wlan0'")
diff --git a/src/op_mode/show_wwan.py b/src/op_mode/show_wwan.py
index eb601a456..bd97bb0e5 100755
--- a/src/op_mode/show_wwan.py
+++ b/src/op_mode/show_wwan.py
@@ -18,7 +18,7 @@ import argparse
from sys import exit
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
+from vyos.utils.process import cmd
parser = argparse.ArgumentParser()
parser.add_argument("--model", help="Get module model", action="store_true")
diff --git a/src/op_mode/show_xdp_stats.sh b/src/op_mode/show_xdp_stats.sh
deleted file mode 100755
index a4ef33107..000000000
--- a/src/op_mode/show_xdp_stats.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-if cli-shell-api existsEffective interfaces $1 $2 xdp; then
- /usr/sbin/xdp_stats --dev "$2"
-else
- echo "XDP not enabled on $2"
-fi
diff --git a/src/op_mode/snmp.py b/src/op_mode/snmp.py
index 5fae67881..43f5d9e0a 100755
--- a/src/op_mode/snmp.py
+++ b/src/op_mode/snmp.py
@@ -24,7 +24,7 @@ import sys
import argparse
from vyos.config import Config
-from vyos.util import call
+from vyos.utils.process import call
config_file_daemon = r'/etc/snmp/snmpd.conf'
diff --git a/src/op_mode/snmp_ifmib.py b/src/op_mode/snmp_ifmib.py
index 2479936bd..c71febac9 100755
--- a/src/op_mode/snmp_ifmib.py
+++ b/src/op_mode/snmp_ifmib.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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
@@ -24,7 +24,7 @@ import argparse
import netifaces
from vyos.config import Config
-from vyos.util import popen
+from vyos.utils.process import popen
parser = argparse.ArgumentParser(description='Retrieve SNMP interfaces information')
parser.add_argument('--ifindex', action='store', nargs='?', const='all', help='Show interface index')
diff --git a/src/op_mode/storage.py b/src/op_mode/storage.py
index d16e271bd..6bc3d3a2d 100755
--- a/src/op_mode/storage.py
+++ b/src/op_mode/storage.py
@@ -18,7 +18,7 @@
import sys
import vyos.opmode
-from vyos.util import cmd
+from vyos.utils.process import cmd
# FIY: As of coreutils from Debian Buster and Bullseye,
# the outpt looks like this:
@@ -43,7 +43,7 @@ def _get_system_storage(only_persistent=False):
def _get_raw_data():
from re import sub as re_sub
- from vyos.util import human_to_bytes
+ from vyos.utils.convert import human_to_bytes
out = _get_system_storage(only_persistent=True)
lines = out.splitlines()
diff --git a/src/op_mode/traceroute.py b/src/op_mode/traceroute.py
index 6c7030ea0..2f0edf53a 100755
--- a/src/op_mode/traceroute.py
+++ b/src/op_mode/traceroute.py
@@ -18,7 +18,7 @@ import os
import sys
import socket
import ipaddress
-from vyos.util import get_all_vrfs
+from vyos.utils.network import get_all_vrfs
from vyos.ifconfig import Section
diff --git a/src/op_mode/uptime.py b/src/op_mode/uptime.py
index 2ebe6783b..d6adf6f4d 100755
--- a/src/op_mode/uptime.py
+++ b/src/op_mode/uptime.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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 as
@@ -20,7 +20,7 @@ import vyos.opmode
def _get_uptime_seconds():
from re import search
- from vyos.util import read_file
+ from vyos.utils.file import read_file
data = read_file("/proc/uptime")
seconds = search("([0-9\.]+)\s", data).group(1)
@@ -29,7 +29,7 @@ def _get_uptime_seconds():
def _get_load_averages():
from re import search
- from vyos.util import cmd
+ from vyos.utils.process import cmd
from vyos.cpu import get_core_count
data = cmd("uptime")
@@ -45,7 +45,7 @@ def _get_load_averages():
return res
def _get_raw_data():
- from vyos.util import seconds_to_human
+ from vyos.utils.convert import seconds_to_human
res = {}
res["uptime_seconds"] = _get_uptime_seconds()
diff --git a/src/op_mode/vpn_ike_sa.py b/src/op_mode/vpn_ike_sa.py
index 4b44c5c15..069c12069 100755
--- a/src/op_mode/vpn_ike_sa.py
+++ b/src/op_mode/vpn_ike_sa.py
@@ -19,7 +19,7 @@ import re
import sys
import vici
-from vyos.util import process_named_running
+from vyos.utils.process import process_named_running
ike_sa_peer_prefix = """\
Peer ID / IP Local ID / IP
@@ -39,8 +39,6 @@ def ike_sa(peer, nat):
peers = []
for conn in sas:
for name, sa in conn.items():
- if peer and not name.startswith('peer_' + peer):
- continue
if name.startswith('peer_') and name in peers:
continue
if nat and 'nat-local' not in sa:
@@ -70,7 +68,7 @@ if __name__ == '__main__':
args = parser.parse_args()
- if not process_named_running('charon'):
+ if not process_named_running('charon-systemd'):
print("IPsec Process NOT Running")
sys.exit(0)
diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py
index b81d1693e..ef89e605f 100755
--- a/src/op_mode/vpn_ipsec.py
+++ b/src/op_mode/vpn_ipsec.py
@@ -17,7 +17,7 @@
import re
import argparse
-from vyos.util import call
+from vyos.utils.process import call
SWANCTL_CONF = '/etc/swanctl/swanctl.conf'
diff --git a/src/op_mode/vrf.py b/src/op_mode/vrf.py
index a9a416761..51032a4b5 100755
--- a/src/op_mode/vrf.py
+++ b/src/op_mode/vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,11 @@ import sys
import typing
from tabulate import tabulate
-from vyos.util import cmd
+from vyos.utils.network import get_vrf_members
+from vyos.utils.process import cmd
import vyos.opmode
-
def _get_raw_data(name=None):
"""
If vrf name is not set - get all VRFs
@@ -45,21 +45,6 @@ def _get_raw_data(name=None):
return data
-def _get_vrf_members(vrf: str) -> list:
- """
- Get list of interface VRF members
- :param vrf: str
- :return: list
- """
- output = cmd(f'ip --json --brief link show master {vrf}')
- answer = json.loads(output)
- interfaces = []
- for data in answer:
- if 'ifname' in data:
- interfaces.append(data.get('ifname'))
- return interfaces if len(interfaces) > 0 else ['n/a']
-
-
def _get_formatted_output(raw_data):
data_entries = []
for vrf in raw_data:
@@ -67,7 +52,9 @@ def _get_formatted_output(raw_data):
state = vrf.get('operstate').lower()
hw_address = vrf.get('address')
flags = ','.join(vrf.get('flags')).lower()
- members = ','.join(_get_vrf_members(name))
+ tmp = get_vrf_members(name)
+ if tmp: members = ','.join(get_vrf_members(name))
+ else: members = 'n/a'
data_entries.append([name, state, hw_address, flags, members])
headers = ["Name", "State", "MAC address", "Flags", "Interfaces"]
diff --git a/src/op_mode/vrrp.py b/src/op_mode/vrrp.py
index dab146d28..b3ab55cc3 100755
--- a/src/op_mode/vrrp.py
+++ b/src/op_mode/vrrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,7 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
import sys
import time
@@ -21,12 +20,10 @@ import argparse
import json
import tabulate
-import vyos.util
-
from vyos.configquery import ConfigTreeQuery
from vyos.ifconfig.vrrp import VRRP
-from vyos.ifconfig.vrrp import VRRPError, VRRPNoData
-
+from vyos.ifconfig.vrrp import VRRPError
+from vyos.ifconfig.vrrp import VRRPNoData
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
@@ -44,7 +41,7 @@ def is_configured():
return True
# Exit early if VRRP is dead or not configured
-if is_configured() == False:
+if is_configured() == False:
print('VRRP not configured!')
exit(0)
if not VRRP.is_running():
diff --git a/src/op_mode/vyos-op-cmd-wrapper.sh b/src/op_mode/vyos-op-cmd-wrapper.sh
new file mode 100755
index 000000000..a89211b2b
--- /dev/null
+++ b/src/op_mode/vyos-op-cmd-wrapper.sh
@@ -0,0 +1,6 @@
+#!/bin/vbash
+shopt -s expand_aliases
+source /etc/default/vyatta
+source /etc/bash_completion.d/vyatta-op
+_vyatta_op_init
+_vyatta_op_run "$@"
diff --git a/src/op_mode/webproxy_update_blacklist.sh b/src/op_mode/webproxy_update_blacklist.sh
index 4fb9a54c6..05ea86f9e 100755
--- a/src/op_mode/webproxy_update_blacklist.sh
+++ b/src/op_mode/webproxy_update_blacklist.sh
@@ -45,6 +45,9 @@ do
--auto-update-blacklist)
auto="yes"
;;
+ --vrf)
+ vrf="yes"
+ ;;
(-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
(*) break;;
esac
@@ -76,7 +79,11 @@ fi
if [[ -n $update ]] && [[ $update -eq "yes" ]]; then
tmp_blacklists='/tmp/blacklists.gz'
- curl -o $tmp_blacklists $blacklist_url
+ if [[ -n $vrf ]] && [[ $vrf -eq "yes" ]]; then
+ sudo ip vrf exec $1 curl -o $tmp_blacklists $blacklist_url
+ else
+ curl -o $tmp_blacklists $blacklist_url
+ fi
if [ $? -ne 0 ]; then
echo "Unable to download [$blacklist_url]!"
exit 1
diff --git a/src/op_mode/wireguard_client.py b/src/op_mode/wireguard_client.py
index 76c1ff7d1..04d8ce28c 100755
--- a/src/op_mode/wireguard_client.py
+++ b/src/op_mode/wireguard_client.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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,8 +23,8 @@ from ipaddress import ip_interface
from vyos.ifconfig import Section
from vyos.template import is_ipv4
from vyos.template import is_ipv6
-from vyos.util import cmd
-from vyos.util import popen
+from vyos.utils.process import cmd
+from vyos.utils.process import popen
if os.geteuid() != 0:
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
diff --git a/src/op_mode/zone.py b/src/op_mode/zone.py
index f326215b1..17ce90396 100755
--- a/src/op_mode/zone.py
+++ b/src/op_mode/zone.py
@@ -19,8 +19,8 @@ import vyos.opmode
import tabulate
from vyos.configquery import ConfigTreeQuery
-from vyos.util import dict_search_args
-from vyos.util import dict_search
+from vyos.utils.dict import dict_search_args
+from vyos.utils.dict import dict_search
def get_config_zone(conf, name=None):
diff --git a/src/pam-configs/radius b/src/pam-configs/radius
index aaae6aeb0..08247f77c 100644
--- a/src/pam-configs/radius
+++ b/src/pam-configs/radius
@@ -1,20 +1,17 @@
Name: RADIUS authentication
-Default: yes
+Default: no
Priority: 257
Auth-Type: Primary
Auth:
- [default=ignore success=1] pam_succeed_if.so uid eq 1000 quiet
- [default=ignore success=ignore] pam_succeed_if.so uid eq 1001 quiet
+ [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
[authinfo_unavail=ignore success=end default=ignore] pam_radius_auth.so
Account-Type: Primary
Account:
- [default=ignore success=1] pam_succeed_if.so uid eq 1000 quiet
- [default=ignore success=ignore] pam_succeed_if.so uid eq 1001 quiet
+ [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
[authinfo_unavail=ignore success=end perm_denied=bad default=ignore] pam_radius_auth.so
Session-Type: Additional
Session:
- [default=ignore success=1] pam_succeed_if.so uid eq 1000 quiet
- [default=ignore success=ignore] pam_succeed_if.so uid eq 1001 quiet
+ [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
[authinfo_unavail=ignore success=ok default=ignore] pam_radius_auth.so
diff --git a/src/pam-configs/tacplus b/src/pam-configs/tacplus
new file mode 100644
index 000000000..66a1eaa4c
--- /dev/null
+++ b/src/pam-configs/tacplus
@@ -0,0 +1,17 @@
+Name: TACACS+ authentication
+Default: no
+Priority: 257
+Auth-Type: Primary
+Auth:
+ [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
+ [authinfo_unavail=ignore success=end auth_err=bad default=ignore] pam_tacplus.so include=/etc/tacplus_servers login=login
+
+Account-Type: Primary
+Account:
+ [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
+ [authinfo_unavail=ignore success=end perm_denied=bad default=ignore] pam_tacplus.so include=/etc/tacplus_servers login=login
+
+Session-Type: Additional
+Session:
+ [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
+ [authinfo_unavail=ignore success=ok default=ignore] pam_tacplus.so include=/etc/tacplus_servers login=login
diff --git a/src/services/api/graphql/generate/schema_from_op_mode.py b/src/services/api/graphql/generate/schema_from_op_mode.py
index 229ccf90f..ab7cb691f 100755
--- a/src/services/api/graphql/generate/schema_from_op_mode.py
+++ b/src/services/api/graphql/generate/schema_from_op_mode.py
@@ -27,7 +27,7 @@ from jinja2 import Template
from vyos.defaults import directories
from vyos.opmode import _is_op_mode_function_name as is_op_mode_function_name
from vyos.opmode import _get_literal_values as get_literal_values
-from vyos.util import load_as_module
+from vyos.utils.system import load_as_module
if __package__ is None or __package__ == '':
sys.path.append(os.path.join(directories['services'], 'api'))
from graphql.libs.op_mode import is_show_function_name
diff --git a/src/services/api/graphql/libs/op_mode.py b/src/services/api/graphql/libs/op_mode.py
index e91d8bd0f..5022f7d4e 100644
--- a/src/services/api/graphql/libs/op_mode.py
+++ b/src/services/api/graphql/libs/op_mode.py
@@ -20,7 +20,7 @@ from typing import Union, Tuple, Optional
from humps import decamelize
from vyos.defaults import directories
-from vyos.util import load_as_module
+from vyos.utils.system import load_as_module
from vyos.opmode import _normalize_field_names
from vyos.opmode import _is_literal_type, _get_literal_values
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 48c9135e2..355182b26 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import os
import sys
@@ -28,8 +26,9 @@ import zmq
from contextlib import contextmanager
from vyos.defaults import directories
-from vyos.util import boot_configuration_complete
-from vyos.configsource import ConfigSourceString, ConfigSourceError
+from vyos.utils.boot import boot_configuration_complete
+from vyos.configsource import ConfigSourceString
+from vyos.configsource import ConfigSourceError
from vyos.config import Config
from vyos import ConfigError
diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd
index 894f9e24d..e34a4b740 100755
--- a/src/services/vyos-hostsd
+++ b/src/services/vyos-hostsd
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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
@@ -241,9 +241,14 @@ import traceback
import re
import logging
import zmq
+
from voluptuous import Schema, MultipleInvalid, Required, Any
from collections import OrderedDict
-from vyos.util import popen, chown, chmod_755, makedir, process_named_running
+from vyos.utils.file import makedir
+from vyos.utils.permission import chown
+from vyos.utils.permission import chmod_755
+from vyos.utils.process import popen
+from vyos.utils.process import process_named_running
from vyos.template import render
debug = True
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index acaa383b4..66e80ced5 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -1,6 +1,6 @@
#!/usr/share/vyos-http-api-tools/bin/python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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
@@ -91,26 +91,20 @@ def success(data):
class ApiModel(BaseModel):
key: StrictStr
-class BaseConfigureModel(BaseModel):
+class BasePathModel(BaseModel):
op: StrictStr
path: List[StrictStr]
- value: StrictStr = None
- @validator("path", pre=True, always=True)
+ @validator("path")
def check_non_empty(cls, path):
- assert len(path) > 0
+ if not len(path) > 0:
+ raise ValueError('path must be non-empty')
return path
-class ConfigureModel(ApiModel):
- op: StrictStr
- path: List[StrictStr]
+class BaseConfigureModel(BasePathModel):
value: StrictStr = None
- @validator("path", pre=True, always=True)
- def check_non_empty(cls, path):
- assert len(path) > 0
- return path
-
+class ConfigureModel(ApiModel, BaseConfigureModel):
class Config:
schema_extra = {
"example": {
@@ -131,6 +125,15 @@ class ConfigureListModel(ApiModel):
}
}
+class BaseConfigSectionModel(BasePathModel):
+ section: Dict
+
+class ConfigSectionModel(ApiModel, BaseConfigSectionModel):
+ pass
+
+class ConfigSectionListModel(ApiModel):
+ commands: List[BaseConfigSectionModel]
+
class RetrieveModel(ApiModel):
op: StrictStr
path: List[StrictStr]
@@ -258,18 +261,15 @@ def auth_required(data: ApiModel):
# the explicit validation may be dropped, if desired, in favor of native
# validation by FastAPI/Pydantic, as is used for application/json requests
class MultipartRequest(Request):
- ERR_MISSING_KEY = False
- ERR_MISSING_DATA = False
- ERR_NOT_JSON = False
- ERR_NOT_DICT = False
- ERR_NO_OP = False
- ERR_NO_PATH = False
- ERR_EMPTY_PATH = False
- ERR_PATH_NOT_LIST = False
- ERR_VALUE_NOT_STRING = False
- ERR_PATH_NOT_LIST_OF_STR = False
- offending_command = {}
- exception = None
+ _form_err = ()
+ @property
+ def form_err(self):
+ return self._form_err
+
+ @form_err.setter
+ def form_err(self, val):
+ if not self._form_err:
+ self._form_err = val
@property
def orig_headers(self):
@@ -308,19 +308,20 @@ class MultipartRequest(Request):
form_data = await self.form()
if form_data:
+ endpoint = self.url.path
logger.debug("processing form data")
for k, v in form_data.multi_items():
forms[k] = v
if 'data' not in forms:
- self.ERR_MISSING_DATA = True
+ self.form_err = (422, "Non-empty data field is required")
+ return self._body
else:
try:
tmp = json.loads(forms['data'])
except json.JSONDecodeError as e:
- self.ERR_NOT_JSON = True
- self.exception = e
- tmp = {}
+ self.form_err = (400, f'Failed to parse JSON: {e}')
+ return self._body
if isinstance(tmp, list):
merge['commands'] = tmp
else:
@@ -334,29 +335,40 @@ class MultipartRequest(Request):
for c in cmds:
if not isinstance(c, dict):
- self.ERR_NOT_DICT = True
- self.offending_command = c
- elif 'op' not in c:
- self.ERR_NO_OP = True
- self.offending_command = c
- elif 'path' not in c:
- self.ERR_NO_PATH = True
- self.offending_command = c
- elif not c['path']:
- self.ERR_EMPTY_PATH = True
- self.offending_command = c
- elif not isinstance(c['path'], list):
- self.ERR_PATH_NOT_LIST = True
- self.offending_command = c
- elif not all(isinstance(el, str) for el in c['path']):
- self.ERR_PATH_NOT_LIST_OF_STR = True
- self.offending_command = c
- elif 'value' in c and not isinstance(c['value'], str):
- self.ERR_VALUE_NOT_STRING = True
- self.offending_command = c
+ self.form_err = (400,
+ f"Malformed command '{c}': any command must be JSON of dict")
+ return self._body
+ if 'op' not in c:
+ self.form_err = (400,
+ f"Malformed command '{c}': missing 'op' field")
+ if endpoint not in ('/config-file', '/container-image',
+ '/image'):
+ if 'path' not in c:
+ self.form_err = (400,
+ f"Malformed command '{c}': missing 'path' field")
+ elif not isinstance(c['path'], list):
+ self.form_err = (400,
+ f"Malformed command '{c}': 'path' field must be a list")
+ elif not all(isinstance(el, str) for el in c['path']):
+ self.form_err = (400,
+ f"Malformed command '{0}': 'path' field must be a list of strings")
+ if endpoint in ('/configure'):
+ if not c['path']:
+ self.form_err = (400,
+ f"Malformed command '{c}': 'path' list must be non-empty")
+ if 'value' in c and not isinstance(c['value'], str):
+ self.form_err = (400,
+ f"Malformed command '{c}': 'value' field must be a string")
+ if endpoint in ('/configure-section'):
+ if 'section' not in c:
+ self.form_err = (400,
+ f"Malformed command '{c}': missing 'section' field")
+ elif not isinstance(c['section'], dict):
+ self.form_err = (400,
+ f"Malformed command '{c}': 'section' field must be JSON of dict")
if 'key' not in forms and 'key' not in merge:
- self.ERR_MISSING_KEY = True
+ self.form_err = (401, "Valid API key is required")
if 'key' in forms and 'key' not in merge:
merge['key'] = forms['key']
@@ -372,40 +384,14 @@ class MultipartRoute(APIRoute):
async def custom_route_handler(request: Request) -> Response:
request = MultipartRequest(request.scope, request.receive)
- endpoint = request.url.path
try:
response: Response = await original_route_handler(request)
except HTTPException as e:
return error(e.status_code, e.detail)
except Exception as e:
- if request.ERR_MISSING_KEY:
- return error(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:
- return error(400, "Failed to parse JSON: {0}".format(request.exception))
- if endpoint == '/configure':
- if request.ERR_NOT_DICT:
- return error(400, "Malformed command \"{0}\": any command must be a dict".format(json.dumps(request.offending_command)))
- if request.ERR_NO_OP:
- return error(400, "Malformed command \"{0}\": missing \"op\" field".format(json.dumps(request.offending_command)))
- if request.ERR_NO_PATH:
- return error(400, "Malformed command \"{0}\": missing \"path\" field".format(json.dumps(request.offending_command)))
- if request.ERR_EMPTY_PATH:
- return error(400, "Malformed command \"{0}\": empty path".format(json.dumps(request.offending_command)))
- if request.ERR_PATH_NOT_LIST:
- return error(400, "Malformed command \"{0}\": \"path\" field must be a list".format(json.dumps(request.offending_command)))
- if request.ERR_VALUE_NOT_STRING:
- return error(400, "Malformed command \"{0}\": \"value\" field must be a string".format(json.dumps(request.offending_command)))
- if request.ERR_PATH_NOT_LIST_OF_STR:
- return error(400, "Malformed command \"{0}\": \"path\" field must be a list of strings".format(json.dumps(request.offending_command)))
- if endpoint in ('/retrieve','/generate','/show','/reset'):
- if request.ERR_NO_OP or request.ERR_NO_PATH:
- return error(400, "Missing required field. \"op\" and \"path\" fields are required")
- if endpoint in ('/config-file', '/image', '/container-image'):
- if request.ERR_NO_OP:
- return error(400, "Missing required field \"op\"")
-
+ form_err = request.form_err
+ if form_err:
+ return error(*form_err)
raise e
return response
@@ -424,12 +410,15 @@ app.router.route_class = MultipartRoute
async def validation_exception_handler(request, exc):
return error(400, str(exc.errors()[0]))
-@app.post('/configure')
-async def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
+def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
+ ConfigSectionModel, ConfigSectionListModel],
+ request: Request):
session = app.state.vyos_session
env = session.get_session_env()
config = vyos.config.Config(session_env=env)
+ endpoint = request.url.path
+
# Allow users to pass just one command
if not isinstance(data, ConfigureListModel):
data = [data]
@@ -442,33 +431,44 @@ async def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
lock.acquire()
status = 200
+ msg = None
error_msg = None
try:
for c in data:
op = c.op
path = c.path
- if c.value:
- value = c.value
- else:
- value = ""
-
- # For vyos.configsession calls that have no separate value arguments,
- # and for type checking too
- cfg_path = " ".join(path + [value]).strip()
-
- if op == 'set':
- # XXX: it would be nice to do a strict check for "path already exists",
- # but there's probably no way to do that
- session.set(path, value=value)
- elif op == 'delete':
- if app.state.vyos_strict and not config.exists(cfg_path):
- raise ConfigSessionError("Cannot delete [{0}]: path/value does not exist".format(cfg_path))
- session.delete(path, value=value)
- elif op == 'comment':
- session.comment(path, value=value)
- else:
- raise ConfigSessionError("\"{0}\" is not a valid operation".format(op))
+ if isinstance(c, BaseConfigureModel):
+ if c.value:
+ value = c.value
+ else:
+ value = ""
+ # For vyos.configsession calls that have no separate value arguments,
+ # and for type checking too
+ cfg_path = " ".join(path + [value]).strip()
+
+ elif isinstance(c, BaseConfigSectionModel):
+ section = c.section
+
+ if isinstance(c, BaseConfigureModel):
+ if op == 'set':
+ session.set(path, value=value)
+ elif op == 'delete':
+ if app.state.vyos_strict and not config.exists(cfg_path):
+ raise ConfigSessionError(f"Cannot delete [{cfg_path}]: path/value does not exist")
+ session.delete(path, value=value)
+ elif op == 'comment':
+ session.comment(path, value=value)
+ else:
+ raise ConfigSessionError(f"'{op}' is not a valid operation")
+
+ elif isinstance(c, BaseConfigSectionModel):
+ if op == 'set':
+ session.set_section(path, section)
+ elif op == 'load':
+ session.load_section(path, section)
+ else:
+ raise ConfigSessionError(f"'{op}' is not a valid operation")
# end for
session.commit()
logger.info(f"Configuration modified via HTTP API using key '{app.state.vyos_id}'")
@@ -491,7 +491,19 @@ async def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
if status != 200:
return error(status, error_msg)
- return success(None)
+ return success(msg)
+
+@app.post('/configure')
+def configure_op(data: Union[ConfigureModel,
+ ConfigureListModel],
+ request: Request):
+ return _configure_op(data, request)
+
+@app.post('/configure-section')
+def configure_section_op(data: Union[ConfigSectionModel,
+ ConfigSectionListModel],
+ request: Request):
+ return _configure_op(data, request)
@app.post("/retrieve")
async def retrieve_op(data: RetrieveModel):
@@ -524,9 +536,9 @@ async def retrieve_op(data: RetrieveModel):
elif config_format == 'raw':
pass
else:
- return error(400, "\"{0}\" is not a valid config format".format(config_format))
+ return error(400, f"'{config_format}' is not a valid config format")
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
@@ -556,7 +568,7 @@ def config_file_op(data: ConfigFileModel):
res = session.migrate_and_load_config(path)
res = session.commit()
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
@@ -585,7 +597,7 @@ def image_op(data: ImageModel):
return error(400, "Missing required field \"name\"")
res = session.remove_image(name)
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
@@ -616,7 +628,7 @@ def image_op(data: ContainerImageModel):
elif op == 'show':
res = session.show_container_image()
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
@@ -636,7 +648,7 @@ def generate_op(data: GenerateModel):
if op == 'generate':
res = session.generate(path)
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
@@ -656,7 +668,7 @@ def show_op(data: ShowModel):
if op == 'show':
res = session.show(path)
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
@@ -676,7 +688,7 @@ def reset_op(data: ResetModel):
if op == 'reset':
res = session.reset(path)
else:
- return error(400, "\"{0}\" is not a valid operation".format(op))
+ return error(400, f"'{op}' is not a valid operation")
except ConfigSessionError as e:
return error(400, str(e))
except Exception as e:
diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py
index 864ee8419..5e19bdbad 100755
--- a/src/system/keepalived-fifo.py
+++ b/src/system/keepalived-fifo.py
@@ -28,9 +28,9 @@ from logging.handlers import SysLogHandler
from vyos.ifconfig.vrrp import VRRP
from vyos.configquery import ConfigTreeQuery
-from vyos.util import cmd
-from vyos.util import dict_search
-from vyos.util import commit_in_progress
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
+from vyos.utils.commit import commit_in_progress
# configure logging
logger = logging.getLogger(__name__)
diff --git a/src/system/vyos-event-handler.py b/src/system/vyos-event-handler.py
index 1c85380bc..74112ec91 100755
--- a/src/system/vyos-event-handler.py
+++ b/src/system/vyos-event-handler.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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,6 +18,7 @@ import argparse
import json
import re
import select
+
from copy import deepcopy
from os import getpid, environ
from pathlib import Path
@@ -25,13 +26,13 @@ from signal import signal, SIGTERM, SIGINT
from sys import exit
from systemd import journal
-from vyos.util import run, dict_search
+from vyos.utils.dict import dict_search
+from vyos.utils.process import run
# Identify this script
my_pid = getpid()
my_name = Path(__file__).stem
-
# handle termination signal
def handle_signal(signal_type, frame):
if signal_type == SIGTERM:
diff --git a/src/system/vyos-system-update-check.py b/src/system/vyos-system-update-check.py
index c9597721b..c874f1e2c 100755
--- a/src/system/vyos-system-update-check.py
+++ b/src/system/vyos-system-update-check.py
@@ -22,7 +22,7 @@ from pathlib import Path
from sys import exit
from time import sleep
-from vyos.util import call
+from vyos.utils.process import call
import vyos.version
diff --git a/src/systemd/dhclient@.service b/src/systemd/dhclient@.service
index 23cd4cfc3..099f7ed52 100644
--- a/src/systemd/dhclient@.service
+++ b/src/systemd/dhclient@.service
@@ -1,18 +1,17 @@
[Unit]
Description=DHCP client on %i
Documentation=man:dhclient(8)
-ConditionPathExists=/var/lib/dhcp/dhclient_%i.conf
-ConditionPathExists=/var/lib/dhcp/dhclient_%i.options
+StartLimitIntervalSec=0
After=vyos-router.service
[Service]
-WorkingDirectory=/var/lib/dhcp
Type=exec
-EnvironmentFile=-/var/lib/dhcp/dhclient_%i.options
-PIDFile=/var/lib/dhcp/dhclient_%i.pid
-ExecStart=/sbin/dhclient -4 $DHCLIENT_OPTS
-ExecStop=/sbin/dhclient -4 $DHCLIENT_OPTS -r
+ExecStart=/sbin/dhclient -4 -d $DHCLIENT_OPTS
+ExecStop=/sbin/dhclient -4 -r $DHCLIENT_OPTS
Restart=always
+RestartPreventExitStatus=
+RestartSec=10
+RuntimeDirectoryPreserve=yes
TimeoutStopSec=20
SendSIGKILL=true
FinalKillSignal=SIGABRT
diff --git a/src/systemd/dhcp6c@.service b/src/systemd/dhcp6c@.service
index 9a97ee261..f634bd944 100644
--- a/src/systemd/dhcp6c@.service
+++ b/src/systemd/dhcp6c@.service
@@ -1,17 +1,19 @@
[Unit]
Description=WIDE DHCPv6 client on %i
Documentation=man:dhcp6c(8) man:dhcp6c.conf(5)
-ConditionPathExists=/run/dhcp6c/dhcp6c.%i.conf
-After=vyos-router.service
StartLimitIntervalSec=0
+After=vyos-router.service
[Service]
-WorkingDirectory=/run/dhcp6c
Type=forking
+WorkingDirectory=/run/dhcp6c
+EnvironmentFile=-/run/dhcp6c/dhcp6c.%i.options
PIDFile=/run/dhcp6c/dhcp6c.%i.pid
-ExecStart=/usr/sbin/dhcp6c -D -k /run/dhcp6c/dhcp6c.%i.sock -c /run/dhcp6c/dhcp6c.%i.conf -p /run/dhcp6c/dhcp6c.%i.pid %i
-Restart=on-failure
-RestartSec=20
+ExecStart=/usr/sbin/dhcp6c $DHCP6C_OPTS
+Restart=always
+RestartPreventExitStatus=
+RestartSec=10
+RuntimeDirectoryPreserve=yes
[Install]
WantedBy=multi-user.target
diff --git a/src/systemd/isc-dhcp-relay6.service b/src/systemd/isc-dhcp-relay6.service
index 30037e013..a365ae4b3 100644
--- a/src/systemd/isc-dhcp-relay6.service
+++ b/src/systemd/isc-dhcp-relay6.service
@@ -5,7 +5,7 @@ Wants=network-online.target
RequiresMountsFor=/run
ConditionPathExists=/run/dhcp-relay/dhcrelay6.conf
After=vyos-router.service
-
+StartLimitIntervalSec=0
[Service]
Type=forking
WorkingDirectory=/run/dhcp-relay
@@ -15,6 +15,6 @@ EnvironmentFile=/run/dhcp-relay/dhcrelay6.conf
PIDFile=/run/dhcp-relay/dhcrelay6.pid
ExecStart=/usr/sbin/dhcrelay -6 -pf /run/dhcp-relay/dhcrelay6.pid $OPTIONS
Restart=always
-
+RestartSec=10
[Install]
WantedBy=multi-user.target
diff --git a/src/systemd/vyos-router.service b/src/systemd/vyos-router.service
new file mode 100644
index 000000000..6f683cebb
--- /dev/null
+++ b/src/systemd/vyos-router.service
@@ -0,0 +1,19 @@
+[Unit]
+Description=VyOS Router
+After=systemd-journald-dev-log.socket time-sync.target local-fs.target cloud-config.service
+Requires=frr.service
+Conflicts=shutdown.target
+Before=systemd-user-sessions.service
+
+[Service]
+Type=simple
+Restart=no
+TimeoutSec=20min
+KillMode=process
+RemainAfterExit=yes
+ExecStart=/usr/libexec/vyos/init/vyos-router start
+ExecStop=/usr/libexec/vyos/init/vyos-router stop
+StandardOutput=journal+console
+
+[Install]
+WantedBy=vyos.target
diff --git a/src/systemd/vyos.target b/src/systemd/vyos.target
new file mode 100644
index 000000000..47c91c1cc
--- /dev/null
+++ b/src/systemd/vyos.target
@@ -0,0 +1,3 @@
+[Unit]
+Description=VyOS target
+After=multi-user.target
diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py
index 6fb43ece2..15ccdf13d 100644
--- a/src/tests/test_configverify.py
+++ b/src/tests/test_configverify.py
@@ -16,7 +16,7 @@
from unittest import TestCase
from vyos.configverify import verify_diffie_hellman_length
-from vyos.util import cmd
+from vyos.utils.process import cmd
dh_file = '/tmp/dh.pem'
diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py
index 1028437b2..2435d89c7 100644
--- a/src/tests/test_dict_search.py
+++ b/src/tests/test_dict_search.py
@@ -15,8 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import TestCase
-from vyos.util import dict_search
-from vyos.util import dict_search_recursive
+from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_recursive
data = {
'string': 'fooo',
diff --git a/src/tests/test_find_device_file.py b/src/tests/test_find_device_file.py
index 43c80dc76..f18043d65 100755
--- a/src/tests/test_find_device_file.py
+++ b/src/tests/test_find_device_file.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import TestCase
-from vyos.util import find_device_file
+from vyos.utils.system import find_device_file
class TestDeviceFile(TestCase):
""" used to find USB devices on target """
diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py
index cb843ff09..ba50d06cc 100644
--- a/src/tests/test_initial_setup.py
+++ b/src/tests/test_initial_setup.py
@@ -21,14 +21,16 @@ import vyos.configtree
import vyos.initialsetup as vis
from unittest import TestCase
-from vyos import xml
+from vyos.xml_ref import definition
+from vyos.xml_ref.pkg_cache.vyos_1x_cache import reference
class TestInitialSetup(TestCase):
def setUp(self):
with open('tests/data/config.boot.default', 'r') as f:
config_string = f.read()
self.config = vyos.configtree.ConfigTree(config_string)
- self.xml = xml.load_configuration()
+ self.xml = definition.Xml()
+ self.xml.define(reference)
def test_set_user_password(self):
vis.set_user_password(self.config, 'vyos', 'vyosvyos')
diff --git a/src/tests/test_util.py b/src/tests/test_utils.py
index 473052bef..9ae329ced 100644
--- a/src/tests/test_util.py
+++ b/src/tests/test_utils.py
@@ -15,14 +15,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import TestCase
-from vyos.util import *
-
-class TestVyOSUtil(TestCase):
- def test_key_mangline(self):
+class TestVyOSUtils(TestCase):
+ def test_key_mangling(self):
+ from vyos.utils.dict import mangle_dict_keys
data = {"foo-bar": {"baz-quux": None}}
expected_data = {"foo_bar": {"baz_quux": None}}
new_data = mangle_dict_keys(data, '-', '_')
self.assertEqual(new_data, expected_data)
def test_sysctl_read(self):
+ from vyos.utils.system import sysctl_read
self.assertEqual(sysctl_read('net.ipv4.conf.lo.forwarding'), '1')
diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py
new file mode 100644
index 000000000..5a6dc2586
--- /dev/null
+++ b/src/tests/test_utils_network.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020-2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import vyos.utils.network
+from unittest import TestCase
+
+class TestVyOSUtilsNetwork(TestCase):
+ def setUp(self):
+ pass
+
+ def test_is_addr_assigned(self):
+ self.assertTrue(vyos.utils.network.is_addr_assigned('127.0.0.1'))
+ self.assertTrue(vyos.utils.network.is_addr_assigned('::1'))
+ self.assertFalse(vyos.utils.network.is_addr_assigned('127.251.255.123'))
+
+ def test_is_ipv6_link_local(self):
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('169.254.0.1'))
+ self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::'))
+ self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1'))
+ self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1%eth0'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::%eth0'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('VyOS'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1%lo'))
+
+ def test_is_ipv6_link_local(self):
+ self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.0.1'))
+ self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.1.1'))
+ self.assertTrue(vyos.utils.network.is_loopback_addr('127.1.1.1'))
+ self.assertTrue(vyos.utils.network.is_loopback_addr('::1'))
+
+ self.assertFalse(vyos.utils.network.is_loopback_addr('::2'))
+ self.assertFalse(vyos.utils.network.is_loopback_addr('192.0.2.1'))
+
+
+
diff --git a/src/tests/test_validate.py b/src/tests/test_validate.py
deleted file mode 100644
index 68a257d25..000000000
--- a/src/tests/test_validate.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import vyos.validate
-from unittest import TestCase
-
-class TestVyOSValidate(TestCase):
- def setUp(self):
- pass
-
- def test_is_addr_assigned(self):
- self.assertTrue(vyos.validate.is_addr_assigned('127.0.0.1'))
- self.assertTrue(vyos.validate.is_addr_assigned('::1'))
- self.assertFalse(vyos.validate.is_addr_assigned('127.251.255.123'))
-
- def test_is_ipv6_link_local(self):
- self.assertFalse(vyos.validate.is_ipv6_link_local('169.254.0.1'))
- self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::'))
- self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::affe:1'))
- self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::affe:1%eth0'))
- self.assertFalse(vyos.validate.is_ipv6_link_local('2001:db8::'))
- self.assertFalse(vyos.validate.is_ipv6_link_local('2001:db8::%eth0'))
- self.assertFalse(vyos.validate.is_ipv6_link_local('VyOS'))
- self.assertFalse(vyos.validate.is_ipv6_link_local('::1'))
- self.assertFalse(vyos.validate.is_ipv6_link_local('::1%lo'))
-
- def test_is_ipv6_link_local(self):
- self.assertTrue(vyos.validate.is_loopback_addr('127.0.0.1'))
- self.assertTrue(vyos.validate.is_loopback_addr('127.0.1.1'))
- self.assertTrue(vyos.validate.is_loopback_addr('127.1.1.1'))
- self.assertTrue(vyos.validate.is_loopback_addr('::1'))
-
- self.assertFalse(vyos.validate.is_loopback_addr('::2'))
- self.assertFalse(vyos.validate.is_loopback_addr('192.0.2.1'))
-
-
-
diff --git a/src/validators/bgp-extended-community b/src/validators/bgp-extended-community
index b69ae3449..d66665519 100755
--- a/src/validators/bgp-extended-community
+++ b/src/validators/bgp-extended-community
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -28,28 +28,27 @@ if __name__ == '__main__':
parser: ArgumentParser = ArgumentParser()
parser.add_argument('community', type=str)
args = parser.parse_args()
- community: str = args.community
- if community.count(':') != 1:
- print("Invalid community format")
- exit(1)
- try:
- # try to extract community parts from an argument
- comm_left: str = community.split(':')[0]
- comm_right: int = int(community.split(':')[1])
-
- # check if left part is an IPv4 address
- if is_ipv4(comm_left) and 0 <= comm_right <= COMM_MAX_2_OCTET:
- exit()
- # check if a left part is a number
- if 0 <= int(comm_left) <= COMM_MAX_2_OCTET \
- and 0 <= comm_right <= COMM_MAX_4_OCTET:
- exit()
-
- except Exception:
- # fail if something was wrong
- print("Invalid community format")
- exit(1)
-
- # fail if none of validators catched the value
- print("Invalid community format")
- exit(1) \ No newline at end of file
+
+ for community in args.community.split():
+ if community.count(':') != 1:
+ print("Invalid community format")
+ exit(1)
+ try:
+ # try to extract community parts from an argument
+ comm_left: str = community.split(':')[0]
+ comm_right: int = int(community.split(':')[1])
+
+ # check if left part is an IPv4 address
+ if is_ipv4(comm_left) and 0 <= comm_right <= COMM_MAX_2_OCTET:
+ continue
+ # check if a left part is a number
+ if 0 <= int(comm_left) <= COMM_MAX_2_OCTET \
+ and 0 <= comm_right <= COMM_MAX_4_OCTET:
+ continue
+
+ raise Exception()
+
+ except Exception:
+ # fail if something was wrong
+ print("Invalid community format")
+ exit(1) \ No newline at end of file
diff --git a/src/validators/ipv6-link-local b/src/validators/ipv6-link-local
index 05e693b77..6ac3ea710 100755
--- a/src/validators/ipv6-link-local
+++ b/src/validators/ipv6-link-local
@@ -1,7 +1,7 @@
#!/usr/bin/python3
import sys
-from vyos.validate import is_ipv6_link_local
+from vyos.utils.network import is_ipv6_link_local
if __name__ == '__main__':
if len(sys.argv)>1:
diff --git a/src/validators/port-multi b/src/validators/port-multi
index bd6f0ef60..ed6ff6849 100755
--- a/src/validators/port-multi
+++ b/src/validators/port-multi
@@ -4,7 +4,7 @@ from sys import argv
from sys import exit
import re
-from vyos.util import read_file
+from vyos.utils.file import read_file
services_file = '/etc/services'
diff --git a/src/validators/port-range b/src/validators/port-range
index 5468000a7..526c639ad 100755
--- a/src/validators/port-range
+++ b/src/validators/port-range
@@ -3,7 +3,7 @@
import sys
import re
-from vyos.util import read_file
+from vyos.utils.file import read_file
services_file = '/etc/services'
diff --git a/src/validators/script b/src/validators/script
index 4ffdeb2a0..eb176d23b 100755
--- a/src/validators/script
+++ b/src/validators/script
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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
@@ -19,7 +19,7 @@ import os
import sys
import shlex
-import vyos.util
+from vyos.utils.file import file_is_persistent
if __name__ == '__main__':
if len(sys.argv) < 2:
@@ -35,7 +35,7 @@ if __name__ == '__main__':
sys.exit(f'File {script} is not an executable file')
# File outside the config dir is just a warning
- if not vyos.util.file_is_persistent(script):
+ if not file_is_persistent(script):
sys.exit(0)(
f'Warning: file {script} is outside the "/config" directory\n'
'It will not be automatically migrated to a new image on system update'
diff --git a/src/validators/timezone b/src/validators/timezone
index 107571181..e55af8d2a 100755
--- a/src/validators/timezone
+++ b/src/validators/timezone
@@ -17,7 +17,7 @@
import argparse
import sys
-from vyos.util import cmd
+from vyos.utils.process import cmd
if __name__ == '__main__':
diff --git a/src/xdp/.gitignore b/src/xdp/.gitignore
deleted file mode 100644
index 2c931cf47..000000000
--- a/src/xdp/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-*.o
-*.ll
-xdp_loader
-xdp_prog_user
-xdp_stats
diff --git a/src/xdp/Makefile b/src/xdp/Makefile
deleted file mode 100644
index 0b5a6eaa0..000000000
--- a/src/xdp/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-
-XDP_TARGETS := xdp_prog_kern
-USER_TARGETS := xdp_prog_user
-
-COMMON_DIR = common
-
-COPY_LOADER := xdp_loader
-COPY_STATS := xdp_stats
-EXTRA_DEPS := $(COMMON_DIR)/parsing_helpers.h
-
-include $(COMMON_DIR)/common.mk
diff --git a/src/xdp/common/Makefile b/src/xdp/common/Makefile
deleted file mode 100644
index 2502027e9..000000000
--- a/src/xdp/common/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0)
-CC := gcc
-
-all: common_params.o common_user_bpf_xdp.o common_libbpf.o
-
-CFLAGS := -g -Wall
-
-CFLAGS += -I../include/
-# TODO: Do we need to make libbpf from this make file too?
-
-common_params.o: common_params.c common_params.h
- $(CC) $(CFLAGS) -c -o $@ $<
-
-common_user_bpf_xdp.o: common_user_bpf_xdp.c common_user_bpf_xdp.h
- $(CC) $(CFLAGS) -c -o $@ $<
-
-common_libbpf.o: common_libbpf.c common_libbpf.h
- $(CC) $(CFLAGS) -c -o $@ $<
-
-.PHONY: clean
-
-clean:
- rm -f *.o
diff --git a/src/xdp/common/README.org b/src/xdp/common/README.org
deleted file mode 100644
index 561fdbced..000000000
--- a/src/xdp/common/README.org
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- fill-column: 76; -*-
-#+TITLE: Common files
-#+OPTIONS: ^:nil
-
-This directory contains code that is common between the different
-assignments. This reduce code duplication in each tutorial assignment, and
-allow us to hideaway code that is irrelevant or have been seen/introduced in
-earlier assignments.
diff --git a/src/xdp/common/common.mk b/src/xdp/common/common.mk
deleted file mode 100644
index ffb86a65c..000000000
--- a/src/xdp/common/common.mk
+++ /dev/null
@@ -1,103 +0,0 @@
-# Common Makefile parts for BPF-building with libbpf
-# --------------------------------------------------
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-#
-# This file should be included from your Makefile like:
-# COMMON_DIR = ../common/
-# include $(COMMON_DIR)/common.mk
-#
-# It is expected that you define the variables:
-# XDP_TARGETS and USER_TARGETS
-# as a space-separated list
-#
-LLC ?= llc
-CLANG ?= clang
-CC ?= gcc
-
-XDP_C = ${XDP_TARGETS:=.c}
-XDP_OBJ = ${XDP_C:.c=.o}
-USER_C := ${USER_TARGETS:=.c}
-USER_OBJ := ${USER_C:.c=.o}
-
-# Expect this is defined by including Makefile, but define if not
-COMMON_DIR ?= ../common/
-
-COPY_LOADER ?=
-LOADER_DIR ?= $(COMMON_DIR)/../utils
-
-# Extend if including Makefile already added some
-COMMON_OBJS += $(COMMON_DIR)/common_params.o $(COMMON_DIR)/common_user_bpf_xdp.o
-
-# Create expansions for dependencies
-COMMON_H := ${COMMON_OBJS:.o=.h}
-
-EXTRA_DEPS +=
-
-# BPF-prog kern and userspace shares struct via header file:
-KERN_USER_H ?= $(wildcard common_kern_user.h)
-
-CFLAGS ?= -g -I../include/
-BPF_CFLAGS ?= -I../include/
-
-LIBS = -lbpf -lelf $(USER_LIBS)
-
-all: llvm-check $(USER_TARGETS) $(XDP_OBJ) $(COPY_LOADER) $(COPY_STATS)
-
-.PHONY: clean $(CLANG) $(LLC)
-
-clean:
- $(MAKE) -C $(COMMON_DIR) clean
- rm -f $(USER_TARGETS) $(XDP_OBJ) $(USER_OBJ) $(COPY_LOADER) $(COPY_STATS)
- rm -f *.ll
- rm -f *~
-
-ifdef COPY_LOADER
-$(COPY_LOADER): $(LOADER_DIR)/${COPY_LOADER:=.c} $(COMMON_H)
- make -C $(LOADER_DIR) $(COPY_LOADER)
- cp $(LOADER_DIR)/$(COPY_LOADER) $(COPY_LOADER)
-endif
-
-ifdef COPY_STATS
-$(COPY_STATS): $(LOADER_DIR)/${COPY_STATS:=.c} $(COMMON_H)
- make -C $(LOADER_DIR) $(COPY_STATS)
- cp $(LOADER_DIR)/$(COPY_STATS) $(COPY_STATS)
-# Needing xdp_stats imply depending on header files:
-EXTRA_DEPS += $(COMMON_DIR)/xdp_stats_kern.h $(COMMON_DIR)/xdp_stats_kern_user.h
-endif
-
-# For build dependency on this file, if it gets updated
-COMMON_MK = $(COMMON_DIR)/common.mk
-
-llvm-check: $(CLANG) $(LLC)
- @for TOOL in $^ ; do \
- if [ ! $$(command -v $${TOOL} 2>/dev/null) ]; then \
- echo "*** ERROR: Cannot find tool $${TOOL}" ;\
- exit 1; \
- else true; fi; \
- done
-
-# Create dependency: detect if C-file change and touch H-file, to trigger
-# target $(COMMON_OBJS)
-$(COMMON_H): %.h: %.c
- touch $@
-
-# Detect if any of common obj changed and create dependency on .h-files
-$(COMMON_OBJS): %.o: %.h
- make -C $(COMMON_DIR)
-
-$(USER_TARGETS): %: %.c Makefile $(COMMON_MK) $(COMMON_OBJS) $(KERN_USER_H) $(EXTRA_DEPS)
- $(CC) -Wall $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) \
- $< $(LIBS)
-
-$(XDP_OBJ): %.o: %.c Makefile $(COMMON_MK) $(KERN_USER_H) $(EXTRA_DEPS)
- $(CLANG) -S \
- -target bpf \
- -D __BPF_TRACING__ \
- $(BPF_CFLAGS) \
- -Wall \
- -Wno-unused-value \
- -Wno-pointer-sign \
- -Wno-compare-distinct-pointer-types \
- -Werror \
- -O2 -emit-llvm -c -g -o ${@:.o=.ll} $<
- $(LLC) -march=bpf -filetype=obj -o $@ ${@:.o=.ll}
diff --git a/src/xdp/common/common_defines.h b/src/xdp/common/common_defines.h
deleted file mode 100644
index 2986d2d67..000000000
--- a/src/xdp/common/common_defines.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef __COMMON_DEFINES_H
-#define __COMMON_DEFINES_H
-
-#include <net/if.h>
-#include <linux/types.h>
-#include <stdbool.h>
-
-struct config {
- __u32 xdp_flags;
- int ifindex;
- char *ifname;
- char ifname_buf[IF_NAMESIZE];
- int redirect_ifindex;
- char *redirect_ifname;
- char redirect_ifname_buf[IF_NAMESIZE];
- bool do_unload;
- bool reuse_maps;
- char pin_dir[512];
- char filename[512];
- char progsec[32];
- char src_mac[18];
- char dest_mac[18];
- __u16 xsk_bind_flags;
- int xsk_if_queue;
- bool xsk_poll_mode;
-};
-
-/* Defined in common_params.o */
-extern int verbose;
-
-/* Exit return codes */
-#define EXIT_OK 0 /* == EXIT_SUCCESS (stdlib.h) man exit(3) */
-#define EXIT_FAIL 1 /* == EXIT_FAILURE (stdlib.h) man exit(3) */
-#define EXIT_FAIL_OPTION 2
-#define EXIT_FAIL_XDP 30
-#define EXIT_FAIL_BPF 40
-
-#endif /* __COMMON_DEFINES_H */
diff --git a/src/xdp/common/common_libbpf.c b/src/xdp/common/common_libbpf.c
deleted file mode 100644
index 443ca4c66..000000000
--- a/src/xdp/common/common_libbpf.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/* Common function that with time should be moved to libbpf */
-
-#include <errno.h>
-#include <string.h>
-
-#include <bpf/bpf.h>
-#include <bpf/libbpf.h>
-
-#include "common_libbpf.h"
-
-/* From: include/linux/err.h */
-#define MAX_ERRNO 4095
-#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
-static inline bool IS_ERR_OR_NULL(const void *ptr)
-{
- return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
-}
-
-#define pr_warning printf
-
-/* As close as possible to libbpf bpf_prog_load_xattr(), with the
- * difference of handling pinned maps.
- */
-int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr,
- struct bpf_object **pobj, int *prog_fd)
-{
- struct bpf_program *prog, *first_prog = NULL;
- enum bpf_attach_type expected_attach_type;
- enum bpf_prog_type prog_type;
- struct bpf_object *obj;
- struct bpf_map *map;
- int err;
- int i;
-
- if (!attr)
- return -EINVAL;
- if (!attr->file)
- return -EINVAL;
-
- obj = bpf_object__open_file(attr->file, NULL);
-
- if (libbpf_get_error(obj))
- return -EINVAL;
-
- prog = bpf_object__next_program(obj, NULL);
- bpf_program__set_type(prog, attr->prog_type);
-
- bpf_object__for_each_program(prog, obj) {
- /*
- * If type is not specified, try to guess it based on
- * section name.
- */
- prog_type = attr->prog_type;
- // Was: prog->prog_ifindex = attr->ifindex;
- bpf_program__set_ifindex(prog, attr->ifindex);
-
- expected_attach_type = attr->expected_attach_type;
-#if 0 /* Use internal libbpf variables */
- if (prog_type == BPF_PROG_TYPE_UNSPEC) {
- err = bpf_program__identify_section(prog, &prog_type,
- &expected_attach_type);
- if (err < 0) {
- bpf_object__close(obj);
- return -EINVAL;
- }
- }
-#endif
-
- bpf_program__set_type(prog, prog_type);
- bpf_program__set_expected_attach_type(prog,
- expected_attach_type);
-
- if (!first_prog)
- first_prog = prog;
- }
-
- /* Reset attr->pinned_maps.map_fd to identify successful file load */
- for (i = 0; i < attr->nr_pinned_maps; i++)
- attr->pinned_maps[i].map_fd = -1;
-
- bpf_map__for_each(map, obj) {
- const char* mapname = bpf_map__name(map);
-
- if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
- bpf_map__set_ifindex(map, attr->ifindex);
- /* Was: map->map_ifindex = attr->ifindex; */
-
- for (i = 0; i < attr->nr_pinned_maps; i++) {
- struct bpf_pinned_map *pin_map = &attr->pinned_maps[i];
- int fd;
-
- if (strcmp(mapname, pin_map->name) != 0)
- continue;
-
- /* Matched, try opening pinned file */
- fd = bpf_obj_get(pin_map->filename);
- if (fd > 0) {
- /* Use FD from pinned map as replacement */
- bpf_map__reuse_fd(map, fd);
- /* TODO: Might want to set internal map "name"
- * if opened pinned map didn't, to allow
- * bpf_object__find_map_fd_by_name() to work.
- */
- pin_map->map_fd = fd;
- continue;
- }
- /* Could not open pinned filename map, then this prog
- * should then pin the map, BUT this can only happen
- * after bpf_object__load().
- */
- }
- }
-
- if (!first_prog) {
- pr_warning("object file doesn't contain bpf program\n");
- bpf_object__close(obj);
- return -ENOENT;
- }
-
- err = bpf_object__load(obj);
- if (err) {
- bpf_object__close(obj);
- return -EINVAL;
- }
-
- /* Pin the maps that were not loaded via pinned filename */
- bpf_map__for_each(map, obj) {
- const char* mapname = bpf_map__name(map);
-
- for (i = 0; i < attr->nr_pinned_maps; i++) {
- struct bpf_pinned_map *pin_map = &attr->pinned_maps[i];
- int err;
-
- if (strcmp(mapname, pin_map->name) != 0)
- continue;
-
- /* Matched, check if map is already loaded */
- if (pin_map->map_fd != -1)
- continue;
-
- /* Needs to be pinned */
- err = bpf_map__pin(map, pin_map->filename);
- if (err)
- continue;
- pin_map->map_fd = bpf_map__fd(map);
- }
- }
-
- /* Help user if requested map name that doesn't exist */
- for (i = 0; i < attr->nr_pinned_maps; i++) {
- struct bpf_pinned_map *pin_map = &attr->pinned_maps[i];
-
- if (pin_map->map_fd < 0)
- pr_warning("%s() requested mapname:%s not seen\n",
- __func__, pin_map->name);
- }
-
- *pobj = obj;
- *prog_fd = bpf_program__fd(first_prog);
- return 0;
-}
diff --git a/src/xdp/common/common_libbpf.h b/src/xdp/common/common_libbpf.h
deleted file mode 100644
index 4754bd8ca..000000000
--- a/src/xdp/common/common_libbpf.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Common function that with time should be moved to libbpf */
-#ifndef __COMMON_LIBBPF_H
-#define __COMMON_LIBBPF_H
-
-struct bpf_pinned_map {
- const char *name;
- const char *filename;
- int map_fd;
-};
-
-/* bpf_prog_load_attr extended */
-struct bpf_prog_load_attr_maps {
- const char *file;
- enum bpf_prog_type prog_type;
- enum bpf_attach_type expected_attach_type;
- int ifindex;
- int nr_pinned_maps;
- struct bpf_pinned_map *pinned_maps;
-};
-
-int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr,
- struct bpf_object **pobj, int *prog_fd);
-
-#endif /* __COMMON_LIBBPF_H */
diff --git a/src/xdp/common/common_params.c b/src/xdp/common/common_params.c
deleted file mode 100644
index 642d56d92..000000000
--- a/src/xdp/common/common_params.c
+++ /dev/null
@@ -1,197 +0,0 @@
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <getopt.h>
-#include <errno.h>
-
-#include <net/if.h>
-#include <linux/if_link.h> /* XDP_FLAGS_* depend on kernel-headers installed */
-#include <linux/if_xdp.h>
-
-#include "common_params.h"
-
-int verbose = 1;
-
-#define BUFSIZE 30
-
-void _print_options(const struct option_wrapper *long_options, bool required)
-{
- int i, pos;
- char buf[BUFSIZE];
-
- for (i = 0; long_options[i].option.name != 0; i++) {
- if (long_options[i].required != required)
- continue;
-
- if (long_options[i].option.val > 64) /* ord('A') = 65 */
- printf(" -%c,", long_options[i].option.val);
- else
- printf(" ");
- pos = snprintf(buf, BUFSIZE, " --%s", long_options[i].option.name);
- if (long_options[i].metavar)
- snprintf(&buf[pos], BUFSIZE-pos, " %s", long_options[i].metavar);
- printf("%-22s", buf);
- printf(" %s", long_options[i].help);
- printf("\n");
- }
-}
-
-void usage(const char *prog_name, const char *doc,
- const struct option_wrapper *long_options, bool full)
-{
- printf("Usage: %s [options]\n", prog_name);
-
- if (!full) {
- printf("Use --help (or -h) to see full option list.\n");
- return;
- }
-
- printf("\nDOCUMENTATION:\n %s\n", doc);
- printf("Required options:\n");
- _print_options(long_options, true);
- printf("\n");
- printf("Other options:\n");
- _print_options(long_options, false);
- printf("\n");
-}
-
-int option_wrappers_to_options(const struct option_wrapper *wrapper,
- struct option **options)
-{
- int i, num;
- struct option *new_options;
- for (i = 0; wrapper[i].option.name != 0; i++) {}
- num = i;
-
- new_options = malloc(sizeof(struct option) * num);
- if (!new_options)
- return -1;
- for (i = 0; i < num; i++) {
- memcpy(&new_options[i], &wrapper[i], sizeof(struct option));
- }
-
- *options = new_options;
- return 0;
-}
-
-void parse_cmdline_args(int argc, char **argv,
- const struct option_wrapper *options_wrapper,
- struct config *cfg, const char *doc)
-{
- struct option *long_options;
- bool full_help = false;
- int longindex = 0;
- char *dest;
- int opt;
-
- if (option_wrappers_to_options(options_wrapper, &long_options)) {
- fprintf(stderr, "Unable to malloc()\n");
- exit(EXIT_FAIL_OPTION);
- }
-
- /* Parse commands line args */
- while ((opt = getopt_long(argc, argv, "hd:r:L:R:ASNFUMQ:czpq",
- long_options, &longindex)) != -1) {
- switch (opt) {
- case 'd':
- if (strlen(optarg) >= IF_NAMESIZE) {
- fprintf(stderr, "ERR: --dev name too long\n");
- goto error;
- }
- cfg->ifname = (char *)&cfg->ifname_buf;
- strncpy(cfg->ifname, optarg, IF_NAMESIZE);
- cfg->ifindex = if_nametoindex(cfg->ifname);
- if (cfg->ifindex == 0) {
- fprintf(stderr,
- "ERR: --dev name unknown err(%d):%s\n",
- errno, strerror(errno));
- goto error;
- }
- break;
- case 'r':
- if (strlen(optarg) >= IF_NAMESIZE) {
- fprintf(stderr, "ERR: --redirect-dev name too long\n");
- goto error;
- }
- cfg->redirect_ifname = (char *)&cfg->redirect_ifname_buf;
- strncpy(cfg->redirect_ifname, optarg, IF_NAMESIZE);
- cfg->redirect_ifindex = if_nametoindex(cfg->redirect_ifname);
- if (cfg->redirect_ifindex == 0) {
- fprintf(stderr,
- "ERR: --redirect-dev name unknown err(%d):%s\n",
- errno, strerror(errno));
- goto error;
- }
- break;
- case 'A':
- cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */
- break;
- case 'S':
- cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */
- cfg->xdp_flags |= XDP_FLAGS_SKB_MODE; /* Set flag */
- cfg->xsk_bind_flags &= XDP_ZEROCOPY;
- cfg->xsk_bind_flags |= XDP_COPY;
- break;
- case 'N':
- cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */
- cfg->xdp_flags |= XDP_FLAGS_DRV_MODE; /* Set flag */
- break;
- case 3: /* --offload-mode */
- cfg->xdp_flags &= ~XDP_FLAGS_MODES; /* Clear flags */
- cfg->xdp_flags |= XDP_FLAGS_HW_MODE; /* Set flag */
- break;
- case 'F':
- cfg->xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
- break;
- case 'M':
- cfg->reuse_maps = true;
- break;
- case 'U':
- cfg->do_unload = true;
- break;
- case 'p':
- cfg->xsk_poll_mode = true;
- break;
- case 'q':
- verbose = false;
- break;
- case 'Q':
- cfg->xsk_if_queue = atoi(optarg);
- break;
- case 1: /* --filename */
- dest = (char *)&cfg->filename;
- strncpy(dest, optarg, sizeof(cfg->filename));
- break;
- case 2: /* --progsec */
- dest = (char *)&cfg->progsec;
- strncpy(dest, optarg, sizeof(cfg->progsec));
- break;
- case 'L': /* --src-mac */
- dest = (char *)&cfg->src_mac;
- strncpy(dest, optarg, sizeof(cfg->src_mac));
- break;
- case 'R': /* --dest-mac */
- dest = (char *)&cfg->dest_mac;
- strncpy(dest, optarg, sizeof(cfg->dest_mac));
- case 'c':
- cfg->xsk_bind_flags &= XDP_ZEROCOPY;
- cfg->xsk_bind_flags |= XDP_COPY;
- break;
- case 'z':
- cfg->xsk_bind_flags &= XDP_COPY;
- cfg->xsk_bind_flags |= XDP_ZEROCOPY;
- break;
- case 'h':
- full_help = true;
- /* fall-through */
- error:
- default:
- usage(argv[0], doc, options_wrapper, full_help);
- free(long_options);
- exit(EXIT_FAIL_OPTION);
- }
- }
- free(long_options);
-}
diff --git a/src/xdp/common/common_params.h b/src/xdp/common/common_params.h
deleted file mode 100644
index 6f64c82e3..000000000
--- a/src/xdp/common/common_params.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* This common_user.h is used by userspace programs */
-#ifndef __COMMON_PARAMS_H
-#define __COMMON_PARAMS_H
-
-#include <getopt.h>
-#include "common_defines.h"
-
-struct option_wrapper {
- struct option option;
- char *help;
- char *metavar;
- bool required;
-};
-
-void usage(const char *prog_name, const char *doc,
- const struct option_wrapper *long_options, bool full);
-
-void parse_cmdline_args(int argc, char **argv,
- const struct option_wrapper *long_options,
- struct config *cfg, const char *doc);
-
-#endif /* __COMMON_PARAMS_H */
diff --git a/src/xdp/common/common_user_bpf_xdp.c b/src/xdp/common/common_user_bpf_xdp.c
deleted file mode 100644
index 524f08c9d..000000000
--- a/src/xdp/common/common_user_bpf_xdp.c
+++ /dev/null
@@ -1,389 +0,0 @@
-#include <bpf/libbpf.h> /* bpf_get_link_xdp_id + bpf_set_link_xdp_id */
-#include <string.h> /* strerror */
-#include <net/if.h> /* IF_NAMESIZE */
-#include <stdlib.h> /* exit(3) */
-#include <errno.h>
-
-#include <bpf/bpf.h>
-#include <bpf/libbpf.h>
-
-#include <linux/if_link.h> /* Need XDP flags */
-#include <linux/err.h>
-
-#include "common_defines.h"
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
-{
- int err;
-
- /* libbpf provide the XDP net_device link-level hook attach helper */
- err = bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL);
- if (err == -EEXIST && !(xdp_flags & XDP_FLAGS_UPDATE_IF_NOEXIST)) {
- /* Force mode didn't work, probably because a program of the
- * opposite type is loaded. Let's unload that and try loading
- * again.
- */
-
- __u32 old_flags = xdp_flags;
-
- xdp_flags &= ~XDP_FLAGS_MODES;
- xdp_flags |= (old_flags & XDP_FLAGS_SKB_MODE) ? XDP_FLAGS_DRV_MODE : XDP_FLAGS_SKB_MODE;
- err = bpf_xdp_detach(ifindex, xdp_flags, NULL);
- if (!err)
- err = bpf_xdp_attach(ifindex, prog_fd, old_flags, NULL);
- }
- if (err < 0) {
- fprintf(stderr, "ERR: "
- "ifindex(%d) link set xdp fd failed (%d): %s\n",
- ifindex, -err, strerror(-err));
-
- switch (-err) {
- case EBUSY:
- case EEXIST:
- fprintf(stderr, "Hint: XDP already loaded on device"
- " use --force to swap/replace\n");
- break;
- case EOPNOTSUPP:
- fprintf(stderr, "Hint: Native-XDP not supported"
- " use --skb-mode or --auto-mode\n");
- break;
- default:
- break;
- }
- return EXIT_FAIL_XDP;
- }
-
- return EXIT_OK;
-}
-
-int xdp_link_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id)
-{
- __u32 curr_prog_id;
- int err;
-
- err = bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id);
- if (err) {
- fprintf(stderr, "ERR: get link xdp id failed (err=%d): %s\n",
- -err, strerror(-err));
- return EXIT_FAIL_XDP;
- }
-
- if (!curr_prog_id) {
- if (verbose)
- printf("INFO: %s() no curr XDP prog on ifindex:%d\n",
- __func__, ifindex);
- return EXIT_OK;
- }
-
- if (expected_prog_id && curr_prog_id != expected_prog_id) {
- fprintf(stderr, "ERR: %s() "
- "expected prog ID(%d) no match(%d), not removing\n",
- __func__, expected_prog_id, curr_prog_id);
- return EXIT_FAIL;
- }
-
- if ((err = bpf_xdp_detach(ifindex, xdp_flags, NULL)) < 0) {
- fprintf(stderr, "ERR: %s() link set xdp failed (err=%d): %s\n",
- __func__, err, strerror(-err));
- return EXIT_FAIL_XDP;
- }
-
- if (verbose)
- printf("INFO: %s() removed XDP prog ID:%d on ifindex:%d\n",
- __func__, curr_prog_id, ifindex);
-
- return EXIT_OK;
-}
-
-struct bpf_object *load_bpf_object_file(const char *filename, int ifindex)
-{
- int first_prog_fd = -1;
- struct bpf_object *obj;
- int err;
-
- /* This struct allow us to set ifindex, this features is used for
- * hardware offloading XDP programs (note this sets libbpf
- * bpf_program->prog_ifindex and foreach bpf_map->map_ifindex).
- */
- struct bpf_program *prog;
- obj = bpf_object__open_file(filename, NULL);
-
- if (libbpf_get_error(obj))
- return NULL;
-
- prog = bpf_object__next_program(obj, NULL);
- bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
- bpf_program__set_ifindex(prog, ifindex);
-
- /* Use libbpf for extracting BPF byte-code from BPF-ELF object, and
- * loading this into the kernel via bpf-syscall
- */
- err = bpf_object__load(obj);
- if (err) {
- fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
- filename, err, strerror(-err));
- return NULL;
- }
-
- first_prog_fd = bpf_program__fd(prog);
-
- /* Notice how a pointer to a libbpf bpf_object is returned */
- return obj;
-}
-
-static struct bpf_object *open_bpf_object(const char *file, int ifindex)
-{
- int err;
- struct bpf_object *obj;
- struct bpf_map *map;
- struct bpf_program *prog, *first_prog = NULL;
-
- obj = bpf_object__open_file(file, NULL);
-
- if (libbpf_get_error(obj))
- return NULL;
-
- prog = bpf_object__next_program(obj, NULL);
- bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
-
- err = bpf_object__load(obj);
- if (IS_ERR_OR_NULL(obj)) {
- err = -PTR_ERR(obj);
- fprintf(stderr, "ERR: opening BPF-OBJ file(%s) (%d): %s\n",
- file, err, strerror(-err));
- return NULL;
- }
-
- bpf_object__for_each_program(prog, obj) {
- bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
- bpf_program__set_ifindex(prog, ifindex);
- if (!first_prog)
- first_prog = prog;
- }
-
- bpf_object__for_each_map(map, obj) {
- if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
- bpf_map__set_ifindex(map, ifindex);
- }
-
- if (!first_prog) {
- fprintf(stderr, "ERR: file %s contains no programs\n", file);
- return NULL;
- }
-
- return obj;
-}
-
-static int reuse_maps(struct bpf_object *obj, const char *path)
-{
- struct bpf_map *map;
-
- if (!obj)
- return -ENOENT;
-
- if (!path)
- return -EINVAL;
-
- bpf_object__for_each_map(map, obj) {
- int len, err;
- int pinned_map_fd;
- char buf[PATH_MAX];
-
- len = snprintf(buf, PATH_MAX, "%s/%s", path, bpf_map__name(map));
- if (len < 0) {
- return -EINVAL;
- } else if (len >= PATH_MAX) {
- return -ENAMETOOLONG;
- }
-
- pinned_map_fd = bpf_obj_get(buf);
- if (pinned_map_fd < 0)
- return pinned_map_fd;
-
- err = bpf_map__reuse_fd(map, pinned_map_fd);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-struct bpf_object *load_bpf_object_file_reuse_maps(const char *file,
- int ifindex,
- const char *pin_dir)
-{
- int err;
- struct bpf_object *obj;
-
- obj = open_bpf_object(file, ifindex);
- if (!obj) {
- fprintf(stderr, "ERR: failed to open object %s\n", file);
- return NULL;
- }
-
- err = reuse_maps(obj, pin_dir);
- if (err) {
- fprintf(stderr, "ERR: failed to reuse maps for object %s, pin_dir=%s\n",
- file, pin_dir);
- return NULL;
- }
-
- err = bpf_object__load(obj);
- if (err) {
- fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
- file, err, strerror(-err));
- return NULL;
- }
-
- return obj;
-}
-
-struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg)
-{
- struct bpf_program *bpf_prog;
- struct bpf_object *bpf_obj;
- int offload_ifindex = 0;
- int prog_fd = -1;
- int err;
-
- /* If flags indicate hardware offload, supply ifindex */
- if (cfg->xdp_flags & XDP_FLAGS_HW_MODE)
- offload_ifindex = cfg->ifindex;
-
- /* Load the BPF-ELF object file and get back libbpf bpf_object */
- if (cfg->reuse_maps)
- bpf_obj = load_bpf_object_file_reuse_maps(cfg->filename,
- offload_ifindex,
- cfg->pin_dir);
- else
- bpf_obj = load_bpf_object_file(cfg->filename, offload_ifindex);
- if (!bpf_obj) {
- fprintf(stderr, "ERR: loading file: %s\n", cfg->filename);
- exit(EXIT_FAIL_BPF);
- }
- /* At this point: All XDP/BPF programs from the cfg->filename have been
- * loaded into the kernel, and evaluated by the verifier. Only one of
- * these gets attached to XDP hook, the others will get freed once this
- * process exit.
- */
-
- if (cfg->progsec[0])
- /* Find a matching BPF prog section name */
- bpf_prog = bpf_object__find_program_by_name(bpf_obj, cfg->progsec);
- else
- /* Find the first program */
- bpf_prog = bpf_object__next_program(bpf_obj, NULL);
-
- if (!bpf_prog) {
- fprintf(stderr, "ERR: couldn't find a program in ELF section '%s'\n", cfg->progsec);
- exit(EXIT_FAIL_BPF);
- }
-
- strncpy(cfg->progsec, bpf_program__section_name(bpf_prog), sizeof(cfg->progsec));
-
- prog_fd = bpf_program__fd(bpf_prog);
- if (prog_fd <= 0) {
- fprintf(stderr, "ERR: bpf_program__fd failed\n");
- exit(EXIT_FAIL_BPF);
- }
-
- /* At this point: BPF-progs are (only) loaded by the kernel, and prog_fd
- * is our select file-descriptor handle. Next step is attaching this FD
- * to a kernel hook point, in this case XDP net_device link-level hook.
- */
- err = xdp_link_attach(cfg->ifindex, cfg->xdp_flags, prog_fd);
- if (err)
- exit(err);
-
- return bpf_obj;
-}
-
-#define XDP_UNKNOWN XDP_REDIRECT + 1
-#ifndef XDP_ACTION_MAX
-#define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
-#endif
-
-static const char *xdp_action_names[XDP_ACTION_MAX] = {
- [XDP_ABORTED] = "XDP_ABORTED",
- [XDP_DROP] = "XDP_DROP",
- [XDP_PASS] = "XDP_PASS",
- [XDP_TX] = "XDP_TX",
- [XDP_REDIRECT] = "XDP_REDIRECT",
- [XDP_UNKNOWN] = "XDP_UNKNOWN",
-};
-
-const char *action2str(__u32 action)
-{
- if (action < XDP_ACTION_MAX)
- return xdp_action_names[action];
- return NULL;
-}
-
-int check_map_fd_info(const struct bpf_map_info *info,
- const struct bpf_map_info *exp)
-{
- if (exp->key_size && exp->key_size != info->key_size) {
- fprintf(stderr, "ERR: %s() "
- "Map key size(%d) mismatch expected size(%d)\n",
- __func__, info->key_size, exp->key_size);
- return EXIT_FAIL;
- }
- if (exp->value_size && exp->value_size != info->value_size) {
- fprintf(stderr, "ERR: %s() "
- "Map value size(%d) mismatch expected size(%d)\n",
- __func__, info->value_size, exp->value_size);
- return EXIT_FAIL;
- }
- if (exp->max_entries && exp->max_entries != info->max_entries) {
- fprintf(stderr, "ERR: %s() "
- "Map max_entries(%d) mismatch expected size(%d)\n",
- __func__, info->max_entries, exp->max_entries);
- return EXIT_FAIL;
- }
- if (exp->type && exp->type != info->type) {
- fprintf(stderr, "ERR: %s() "
- "Map type(%d) mismatch expected type(%d)\n",
- __func__, info->type, exp->type);
- return EXIT_FAIL;
- }
-
- return 0;
-}
-
-int open_bpf_map_file(const char *pin_dir,
- const char *mapname,
- struct bpf_map_info *info)
-{
- char filename[PATH_MAX];
- int err, len, fd;
- __u32 info_len = sizeof(*info);
-
- len = snprintf(filename, PATH_MAX, "%s/%s", pin_dir, mapname);
- if (len < 0) {
- fprintf(stderr, "ERR: constructing full mapname path\n");
- return -1;
- }
-
- fd = bpf_obj_get(filename);
- if (fd < 0) {
- fprintf(stderr,
- "WARN: Failed to open bpf map file:%s err(%d):%s\n",
- filename, errno, strerror(errno));
- return fd;
- }
-
- if (info) {
- err = bpf_obj_get_info_by_fd(fd, info, &info_len);
- if (err) {
- fprintf(stderr, "ERR: %s() can't get info - %s\n",
- __func__, strerror(errno));
- return EXIT_FAIL_BPF;
- }
- }
-
- return fd;
-}
diff --git a/src/xdp/common/common_user_bpf_xdp.h b/src/xdp/common/common_user_bpf_xdp.h
deleted file mode 100644
index 4f8aa51d4..000000000
--- a/src/xdp/common/common_user_bpf_xdp.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Common BPF/XDP functions used by userspace side programs */
-#ifndef __COMMON_USER_BPF_XDP_H
-#define __COMMON_USER_BPF_XDP_H
-
-int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd);
-int xdp_link_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id);
-
-struct bpf_object *load_bpf_object_file(const char *filename, int ifindex);
-struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg);
-
-const char *action2str(__u32 action);
-
-int check_map_fd_info(const struct bpf_map_info *info,
- const struct bpf_map_info *exp);
-
-int open_bpf_map_file(const char *pin_dir,
- const char *mapname,
- struct bpf_map_info *info);
-
-#endif /* __COMMON_USER_BPF_XDP_H */
diff --git a/src/xdp/common/parsing_helpers.h b/src/xdp/common/parsing_helpers.h
deleted file mode 100644
index c0837e88f..000000000
--- a/src/xdp/common/parsing_helpers.h
+++ /dev/null
@@ -1,244 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
-/*
- * This file contains parsing functions that are used in the packetXX XDP
- * programs. The functions are marked as __always_inline, and fully defined in
- * this header file to be included in the BPF program.
- *
- * Each helper parses a packet header, including doing bounds checking, and
- * returns the type of its contents if successful, and -1 otherwise.
- *
- * For Ethernet and IP headers, the content type is the type of the payload
- * (h_proto for Ethernet, nexthdr for IPv6), for ICMP it is the ICMP type field.
- * All return values are in host byte order.
- *
- * The versions of the functions included here are slightly expanded versions of
- * the functions in the packet01 lesson. For instance, the Ethernet header
- * parsing has support for parsing VLAN tags.
- */
-
-#ifndef __PARSING_HELPERS_H
-#define __PARSING_HELPERS_H
-
-#include <stddef.h>
-#include <linux/if_ether.h>
-#include <linux/if_packet.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/icmp.h>
-#include <linux/icmpv6.h>
-#include <linux/udp.h>
-#include <linux/tcp.h>
-
-/* Header cursor to keep track of current parsing position */
-struct hdr_cursor {
- void *pos;
-};
-
-/*
- * struct vlan_hdr - vlan header
- * @h_vlan_TCI: priority and VLAN ID
- * @h_vlan_encapsulated_proto: packet type ID or len
- */
-struct vlan_hdr {
- __be16 h_vlan_TCI;
- __be16 h_vlan_encapsulated_proto;
-};
-
-/*
- * Struct icmphdr_common represents the common part of the icmphdr and icmp6hdr
- * structures.
- */
-struct icmphdr_common {
- __u8 type;
- __u8 code;
- __sum16 cksum;
-};
-
-/* Allow users of header file to redefine VLAN max depth */
-#ifndef VLAN_MAX_DEPTH
-#define VLAN_MAX_DEPTH 4
-#endif
-
-static __always_inline int proto_is_vlan(__u16 h_proto)
-{
- return !!(h_proto == bpf_htons(ETH_P_8021Q) ||
- h_proto == bpf_htons(ETH_P_8021AD));
-}
-
-/* Notice, parse_ethhdr() will skip VLAN tags, by advancing nh->pos and returns
- * next header EtherType, BUT the ethhdr pointer supplied still points to the
- * Ethernet header. Thus, caller can look at eth->h_proto to see if this was a
- * VLAN tagged packet.
- */
-static __always_inline int parse_ethhdr(struct hdr_cursor *nh, void *data_end,
- struct ethhdr **ethhdr)
-{
- struct ethhdr *eth = nh->pos;
- int hdrsize = sizeof(*eth);
- struct vlan_hdr *vlh;
- __u16 h_proto;
- int i;
-
- /* Byte-count bounds check; check if current pointer + size of header
- * is after data_end.
- */
- if (nh->pos + hdrsize > data_end)
- return -1;
-
- nh->pos += hdrsize;
- *ethhdr = eth;
- vlh = nh->pos;
- h_proto = eth->h_proto;
-
- /* Use loop unrolling to avoid the verifier restriction on loops;
- * support up to VLAN_MAX_DEPTH layers of VLAN encapsulation.
- */
- #pragma unroll
- for (i = 0; i < VLAN_MAX_DEPTH; i++) {
- if (!proto_is_vlan(h_proto))
- break;
-
- if (vlh + 1 > data_end)
- break;
-
- h_proto = vlh->h_vlan_encapsulated_proto;
- vlh++;
- }
-
- nh->pos = vlh;
- return h_proto; /* network-byte-order */
-}
-
-static __always_inline int parse_ip6hdr(struct hdr_cursor *nh,
- void *data_end,
- struct ipv6hdr **ip6hdr)
-{
- struct ipv6hdr *ip6h = nh->pos;
-
- /* Pointer-arithmetic bounds check; pointer +1 points to after end of
- * thing being pointed to. We will be using this style in the remainder
- * of the tutorial.
- */
- if (ip6h + 1 > data_end)
- return -1;
-
- nh->pos = ip6h + 1;
- *ip6hdr = ip6h;
-
- return ip6h->nexthdr;
-}
-
-static __always_inline int parse_iphdr(struct hdr_cursor *nh,
- void *data_end,
- struct iphdr **iphdr)
-{
- struct iphdr *iph = nh->pos;
- int hdrsize;
-
- if (iph + 1 > data_end)
- return -1;
-
- hdrsize = iph->ihl * 4;
-
- /* Variable-length IPv4 header, need to use byte-based arithmetic */
- if (nh->pos + hdrsize > data_end)
- return -1;
-
- nh->pos += hdrsize;
- *iphdr = iph;
-
- return iph->protocol;
-}
-
-static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh,
- void *data_end,
- struct icmp6hdr **icmp6hdr)
-{
- struct icmp6hdr *icmp6h = nh->pos;
-
- if (icmp6h + 1 > data_end)
- return -1;
-
- nh->pos = icmp6h + 1;
- *icmp6hdr = icmp6h;
-
- return icmp6h->icmp6_type;
-}
-
-static __always_inline int parse_icmphdr(struct hdr_cursor *nh,
- void *data_end,
- struct icmphdr **icmphdr)
-{
- struct icmphdr *icmph = nh->pos;
-
- if (icmph + 1 > data_end)
- return -1;
-
- nh->pos = icmph + 1;
- *icmphdr = icmph;
-
- return icmph->type;
-}
-
-static __always_inline int parse_icmphdr_common(struct hdr_cursor *nh,
- void *data_end,
- struct icmphdr_common **icmphdr)
-{
- struct icmphdr_common *h = nh->pos;
-
- if (h + 1 > data_end)
- return -1;
-
- nh->pos = h + 1;
- *icmphdr = h;
-
- return h->type;
-}
-
-/*
- * parse_udphdr: parse the udp header and return the length of the udp payload
- */
-static __always_inline int parse_udphdr(struct hdr_cursor *nh,
- void *data_end,
- struct udphdr **udphdr)
-{
- int len;
- struct udphdr *h = nh->pos;
-
- if (h + 1 > data_end)
- return -1;
-
- nh->pos = h + 1;
- *udphdr = h;
-
- len = bpf_ntohs(h->len) - sizeof(struct udphdr);
- if (len < 0)
- return -1;
-
- return len;
-}
-
-/*
- * parse_tcphdr: parse and return the length of the tcp header
- */
-static __always_inline int parse_tcphdr(struct hdr_cursor *nh,
- void *data_end,
- struct tcphdr **tcphdr)
-{
- int len;
- struct tcphdr *h = nh->pos;
-
- if (h + 1 > data_end)
- return -1;
-
- len = h->doff * 4;
- if ((void *) h + len > data_end)
- return -1;
-
- nh->pos = h + 1;
- *tcphdr = h;
-
- return len;
-}
-
-#endif /* __PARSING_HELPERS_H */
diff --git a/src/xdp/common/rewrite_helpers.h b/src/xdp/common/rewrite_helpers.h
deleted file mode 100644
index a5d3e671d..000000000
--- a/src/xdp/common/rewrite_helpers.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
-/*
- * This file contains functions that are used in the packetXX XDP programs to
- * manipulate on packets data. The functions are marked as __always_inline, and
- * fully defined in this header file to be included in the BPF program.
- */
-
-#ifndef __REWRITE_HELPERS_H
-#define __REWRITE_HELPERS_H
-
-#include <linux/bpf.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/if_ether.h>
-
-#include <bpf/bpf_helpers.h>
-#include <bpf/bpf_endian.h>
-
-/* Pops the outermost VLAN tag off the packet. Returns the popped VLAN ID on
- * success or negative errno on failure.
- */
-static __always_inline int vlan_tag_pop(struct xdp_md *ctx, struct ethhdr *eth)
-{
- void *data_end = (void *)(long)ctx->data_end;
- struct ethhdr eth_cpy;
- struct vlan_hdr *vlh;
- __be16 h_proto;
- int vlid;
-
- if (!proto_is_vlan(eth->h_proto))
- return -1;
-
- /* Careful with the parenthesis here */
- vlh = (void *)(eth + 1);
-
- /* Still need to do bounds checking */
- if (vlh + 1 > data_end)
- return -1;
-
- /* Save vlan ID for returning, h_proto for updating Ethernet header */
- vlid = bpf_ntohs(vlh->h_vlan_TCI);
- h_proto = vlh->h_vlan_encapsulated_proto;
-
- /* Make a copy of the outer Ethernet header before we cut it off */
- __builtin_memcpy(&eth_cpy, eth, sizeof(eth_cpy));
-
- /* Actually adjust the head pointer */
- if (bpf_xdp_adjust_head(ctx, (int)sizeof(*vlh)))
- return -1;
-
- /* Need to re-evaluate data *and* data_end and do new bounds checking
- * after adjusting head
- */
- eth = (void *)(long)ctx->data;
- data_end = (void *)(long)ctx->data_end;
- if (eth + 1 > data_end)
- return -1;
-
- /* Copy back the old Ethernet header and update the proto type */
- __builtin_memcpy(eth, &eth_cpy, sizeof(*eth));
- eth->h_proto = h_proto;
-
- return vlid;
-}
-
-/* Pushes a new VLAN tag after the Ethernet header. Returns 0 on success,
- * -1 on failure.
- */
-static __always_inline int vlan_tag_push(struct xdp_md *ctx,
- struct ethhdr *eth, int vlid)
-{
- void *data_end = (void *)(long)ctx->data_end;
- struct ethhdr eth_cpy;
- struct vlan_hdr *vlh;
-
- /* First copy the original Ethernet header */
- __builtin_memcpy(&eth_cpy, eth, sizeof(eth_cpy));
-
- /* Then add space in front of the packet */
- if (bpf_xdp_adjust_head(ctx, 0 - (int)sizeof(*vlh)))
- return -1;
-
- /* Need to re-evaluate data_end and data after head adjustment, and
- * bounds check, even though we know there is enough space (as we
- * increased it).
- */
- data_end = (void *)(long)ctx->data_end;
- eth = (void *)(long)ctx->data;
-
- if (eth + 1 > data_end)
- return -1;
-
- /* Copy back Ethernet header in the right place, populate VLAN tag with
- * ID and proto, and set outer Ethernet header to VLAN type.
- */
- __builtin_memcpy(eth, &eth_cpy, sizeof(*eth));
-
- vlh = (void *)(eth + 1);
-
- if (vlh + 1 > data_end)
- return -1;
-
- vlh->h_vlan_TCI = bpf_htons(vlid);
- vlh->h_vlan_encapsulated_proto = eth->h_proto;
-
- eth->h_proto = bpf_htons(ETH_P_8021Q);
- return 0;
-}
-
-/*
- * Swaps destination and source MAC addresses inside an Ethernet header
- */
-static __always_inline void swap_src_dst_mac(struct ethhdr *eth)
-{
- __u8 h_tmp[ETH_ALEN];
-
- __builtin_memcpy(h_tmp, eth->h_source, ETH_ALEN);
- __builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN);
- __builtin_memcpy(eth->h_dest, h_tmp, ETH_ALEN);
-}
-
-/*
- * Swaps destination and source IPv6 addresses inside an IPv6 header
- */
-static __always_inline void swap_src_dst_ipv6(struct ipv6hdr *ipv6)
-{
- struct in6_addr tmp = ipv6->saddr;
-
- ipv6->saddr = ipv6->daddr;
- ipv6->daddr = tmp;
-}
-
-/*
- * Swaps destination and source IPv4 addresses inside an IPv4 header
- */
-static __always_inline void swap_src_dst_ipv4(struct iphdr *iphdr)
-{
- __be32 tmp = iphdr->saddr;
-
- iphdr->saddr = iphdr->daddr;
- iphdr->daddr = tmp;
-}
-
-#endif /* __REWRITE_HELPERS_H */
diff --git a/src/xdp/common/xdp_stats_kern.h b/src/xdp/common/xdp_stats_kern.h
deleted file mode 100644
index c061a149d..000000000
--- a/src/xdp/common/xdp_stats_kern.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-/* Used *ONLY* by BPF-prog running kernel side. */
-#ifndef __XDP_STATS_KERN_H
-#define __XDP_STATS_KERN_H
-
-/* Data record type 'struct datarec' is defined in common/xdp_stats_kern_user.h,
- * programs using this header must first include that file.
- */
-#ifndef __XDP_STATS_KERN_USER_H
-#warning "You forgot to #include <../common/xdp_stats_kern_user.h>"
-#include <../common/xdp_stats_kern_user.h>
-#endif
-
-/* Keeps stats per (enum) xdp_action */
-struct {
- __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
- __type(key, __u32);
- __type(value, struct datarec);
- __uint(max_entries, XDP_ACTION_MAX);
-} xdp_stats_map SEC(".maps");
-
-static __always_inline
-__u32 xdp_stats_record_action(struct xdp_md *ctx, __u32 action)
-{
- if (action >= XDP_ACTION_MAX)
- return XDP_ABORTED;
-
- /* Lookup in kernel BPF-side return pointer to actual data record */
- struct datarec *rec = bpf_map_lookup_elem(&xdp_stats_map, &action);
- if (!rec)
- return XDP_ABORTED;
-
- /* BPF_MAP_TYPE_PERCPU_ARRAY returns a data record specific to current
- * CPU and XDP hooks runs under Softirq, which makes it safe to update
- * without atomic operations.
- */
- rec->rx_packets++;
- rec->rx_bytes += (ctx->data_end - ctx->data);
-
- return action;
-}
-
-#endif /* __XDP_STATS_KERN_H */
diff --git a/src/xdp/common/xdp_stats_kern_user.h b/src/xdp/common/xdp_stats_kern_user.h
deleted file mode 100644
index d7b8d05e6..000000000
--- a/src/xdp/common/xdp_stats_kern_user.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-/* Used by BPF-prog kernel side BPF-progs and userspace programs,
- * for sharing xdp_stats common struct and DEFINEs.
- */
-#ifndef __XDP_STATS_KERN_USER_H
-#define __XDP_STATS_KERN_USER_H
-
-/* This is the data record stored in the map */
-struct datarec {
- __u64 rx_packets;
- __u64 rx_bytes;
-};
-
-#ifndef XDP_ACTION_MAX
-#define XDP_ACTION_MAX (XDP_REDIRECT + 1)
-#endif
-
-#endif /* __XDP_STATS_KERN_USER_H */
diff --git a/src/xdp/include/bpf_endian.h b/src/xdp/include/bpf_endian.h
deleted file mode 100644
index 2b0ede3d5..000000000
--- a/src/xdp/include/bpf_endian.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_endian.h */
-#ifndef __BPF_ENDIAN__
-#define __BPF_ENDIAN__
-
-#include <linux/swab.h>
-
-/* LLVM's BPF target selects the endianness of the CPU
- * it compiles on, or the user specifies (bpfel/bpfeb),
- * respectively. The used __BYTE_ORDER__ is defined by
- * the compiler, we cannot rely on __BYTE_ORDER from
- * libc headers, since it doesn't reflect the actual
- * requested byte order.
- *
- * Note, LLVM's BPF target has different __builtin_bswapX()
- * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE
- * in bpfel and bpfeb case, which means below, that we map
- * to cpu_to_be16(). We could use it unconditionally in BPF
- * case, but better not rely on it, so that this header here
- * can be used from application and BPF program side, which
- * use different targets.
- */
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-# define __bpf_ntohs(x)__builtin_bswap16(x)
-# define __bpf_htons(x)__builtin_bswap16(x)
-# define __bpf_constant_ntohs(x)___constant_swab16(x)
-# define __bpf_constant_htons(x)___constant_swab16(x)
-# define __bpf_ntohl(x)__builtin_bswap32(x)
-# define __bpf_htonl(x)__builtin_bswap32(x)
-# define __bpf_constant_ntohl(x)___constant_swab32(x)
-# define __bpf_constant_htonl(x)___constant_swab32(x)
-#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-# define __bpf_ntohs(x)(x)
-# define __bpf_htons(x)(x)
-# define __bpf_constant_ntohs(x)(x)
-# define __bpf_constant_htons(x)(x)
-# define __bpf_ntohl(x)(x)
-# define __bpf_htonl(x)(x)
-# define __bpf_constant_ntohl(x)(x)
-# define __bpf_constant_htonl(x)(x)
-#else
-# error "Fix your compiler's __BYTE_ORDER__?!"
-#endif
-
-#define bpf_htons(x)\
- (__builtin_constant_p(x) ?\
- __bpf_constant_htons(x) : __bpf_htons(x))
-#define bpf_ntohs(x)\
- (__builtin_constant_p(x) ?\
- __bpf_constant_ntohs(x) : __bpf_ntohs(x))
-#define bpf_htonl(x)\
- (__builtin_constant_p(x) ?\
- __bpf_constant_htonl(x) : __bpf_htonl(x))
-#define bpf_ntohl(x)\
- (__builtin_constant_p(x) ?\
- __bpf_constant_ntohl(x) : __bpf_ntohl(x))
-
-#endif /* __BPF_ENDIAN__ */
diff --git a/src/xdp/include/bpf_legacy.h b/src/xdp/include/bpf_legacy.h
deleted file mode 100644
index 8dfa168cd..000000000
--- a/src/xdp/include/bpf_legacy.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
-#ifndef __BPF_LEGACY__
-#define __BPF_LEGACY__
-
-/*
- * legacy bpf_map_def with extra fields supported only by bpf_load(), do not
- * use outside of samples/bpf
- */
-struct bpf_map_def_legacy {
- unsigned int type;
- unsigned int key_size;
- unsigned int value_size;
- unsigned int max_entries;
- unsigned int map_flags;
- unsigned int inner_map_idx;
- unsigned int numa_node;
-};
-
-#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
- struct ____btf_map_##name { \
- type_key key; \
- type_val value; \
- }; \
- struct ____btf_map_##name \
- __attribute__ ((section(".maps." #name), used)) \
- ____btf_map_##name = { }
-
-/* llvm builtin functions that eBPF C program may use to
- * emit BPF_LD_ABS and BPF_LD_IND instructions
- */
-unsigned long long load_byte(void *skb,
- unsigned long long off) asm("llvm.bpf.load.byte");
-unsigned long long load_half(void *skb,
- unsigned long long off) asm("llvm.bpf.load.half");
-unsigned long long load_word(void *skb,
- unsigned long long off) asm("llvm.bpf.load.word");
-
-#endif
diff --git a/src/xdp/include/bpf_util.h b/src/xdp/include/bpf_util.h
deleted file mode 100644
index e74b33e9d..000000000
--- a/src/xdp/include/bpf_util.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_util.h */
-#ifndef __BPF_UTIL__
-#define __BPF_UTIL__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-static inline unsigned int bpf_num_possible_cpus(void)
-{
- static const char *fcpu = "/sys/devices/system/cpu/possible";
- unsigned int start, end, possible_cpus = 0;
- char buff[128];
- FILE *fp;
- int n;
-
- fp = fopen(fcpu, "r");
- if (!fp) {
- printf("Failed to open %s: '%s'!\n", fcpu, strerror(errno));
- exit(1);
- }
-
- while (fgets(buff, sizeof(buff), fp)) {
- n = sscanf(buff, "%u-%u", &start, &end);
- if (n == 0) {
- printf("Failed to retrieve # possible CPUs!\n");
- exit(1);
- } else if (n == 1) {
- end = start;
- }
- possible_cpus = start == 0 ? end + 1 : 0;
- break;
- }
- fclose(fp);
-
- return possible_cpus;
-}
-
-#define __bpf_percpu_val_align __attribute__((__aligned__(8)))
-
-#define BPF_DECLARE_PERCPU(type, name) \
- struct { type v; /* padding */ } __bpf_percpu_val_align \
- name[bpf_num_possible_cpus()]
-#define bpf_percpu(name, cpu) name[(cpu)].v
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-#endif
-
-#ifndef sizeof_field
-#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
-#endif
-
-#ifndef offsetofend
-#define offsetofend(TYPE, MEMBER) \
- (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
-#endif
-
-#endif /* __BPF_UTIL__ */
diff --git a/src/xdp/include/jhash.h b/src/xdp/include/jhash.h
deleted file mode 100644
index b81a0d0eb..000000000
--- a/src/xdp/include/jhash.h
+++ /dev/null
@@ -1,172 +0,0 @@
-#ifndef _LINUX_JHASH_H
-#define _LINUX_JHASH_H
-
-/* Copied from $(LINUX)/include/linux/jhash.h (kernel 4.18) */
-
-/* jhash.h: Jenkins hash support.
- *
- * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
- *
- * http://burtleburtle.net/bob/hash/
- *
- * These are the credits from Bob's sources:
- *
- * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
- *
- * These are functions for producing 32-bit hashes for hash table lookup.
- * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
- * are externally useful functions. Routines to test the hash are included
- * if SELF_TEST is defined. You can use this free for any purpose. It's in
- * the public domain. It has no warranty.
- *
- * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
- */
-
-static inline __u32 rol32(__u32 word, unsigned int shift)
-{
- return (word << shift) | (word >> ((-shift) & 31));
-}
-
-/* copy paste of jhash from kernel sources (include/linux/jhash.h) to make sure
- * LLVM can compile it into valid sequence of BPF instructions
- */
-#define __jhash_mix(a, b, c) \
-{ \
- a -= c; a ^= rol32(c, 4); c += b; \
- b -= a; b ^= rol32(a, 6); a += c; \
- c -= b; c ^= rol32(b, 8); b += a; \
- a -= c; a ^= rol32(c, 16); c += b; \
- b -= a; b ^= rol32(a, 19); a += c; \
- c -= b; c ^= rol32(b, 4); b += a; \
-}
-
-#define __jhash_final(a, b, c) \
-{ \
- c ^= b; c -= rol32(b, 14); \
- a ^= c; a -= rol32(c, 11); \
- b ^= a; b -= rol32(a, 25); \
- c ^= b; c -= rol32(b, 16); \
- a ^= c; a -= rol32(c, 4); \
- b ^= a; b -= rol32(a, 14); \
- c ^= b; c -= rol32(b, 24); \
-}
-
-#define JHASH_INITVAL 0xdeadbeef
-
-typedef unsigned int u32;
-
-/* jhash - hash an arbitrary key
- * @k: sequence of bytes as key
- * @length: the length of the key
- * @initval: the previous hash, or an arbitray value
- *
- * The generic version, hashes an arbitrary sequence of bytes.
- * No alignment or length assumptions are made about the input key.
- *
- * Returns the hash value of the key. The result depends on endianness.
- */
-static inline u32 jhash(const void *key, u32 length, u32 initval)
-{
- u32 a, b, c;
- const unsigned char *k = key;
-
- /* Set up the internal state */
- a = b = c = JHASH_INITVAL + length + initval;
-
- /* All but the last block: affect some 32 bits of (a,b,c) */
- while (length > 12) {
- a += *(u32 *)(k);
- b += *(u32 *)(k + 4);
- c += *(u32 *)(k + 8);
- __jhash_mix(a, b, c);
- length -= 12;
- k += 12;
- }
- /* Last block: affect all 32 bits of (c) */
- switch (length) {
- case 12: c += (u32)k[11]<<24; /* fall through */
- case 11: c += (u32)k[10]<<16; /* fall through */
- case 10: c += (u32)k[9]<<8; /* fall through */
- case 9: c += k[8]; /* fall through */
- case 8: b += (u32)k[7]<<24; /* fall through */
- case 7: b += (u32)k[6]<<16; /* fall through */
- case 6: b += (u32)k[5]<<8; /* fall through */
- case 5: b += k[4]; /* fall through */
- case 4: a += (u32)k[3]<<24; /* fall through */
- case 3: a += (u32)k[2]<<16; /* fall through */
- case 2: a += (u32)k[1]<<8; /* fall through */
- case 1: a += k[0];
- __jhash_final(a, b, c);
- case 0: /* Nothing left to add */
- break;
- }
-
- return c;
-}
-
-/* jhash2 - hash an array of u32's
- * @k: the key which must be an array of u32's
- * @length: the number of u32's in the key
- * @initval: the previous hash, or an arbitray value
- *
- * Returns the hash value of the key.
- */
-static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
-{
- u32 a, b, c;
-
- /* Set up the internal state */
- a = b = c = JHASH_INITVAL + (length<<2) + initval;
-
- /* Handle most of the key */
- while (length > 3) {
- a += k[0];
- b += k[1];
- c += k[2];
- __jhash_mix(a, b, c);
- length -= 3;
- k += 3;
- }
-
- /* Handle the last 3 u32's */
- switch (length) {
- case 3: c += k[2]; /* fall through */
- case 2: b += k[1]; /* fall through */
- case 1: a += k[0];
- __jhash_final(a, b, c);
- case 0: /* Nothing left to add */
- break;
- }
-
- return c;
-}
-
-
-/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
-static inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval)
-{
- a += initval;
- b += initval;
- c += initval;
-
- __jhash_final(a, b, c);
-
- return c;
-}
-
-static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
-{
- return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2));
-}
-
-static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
-{
- return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
-}
-
-static inline u32 jhash_1word(u32 a, u32 initval)
-{
- return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2));
-}
-
-#endif /* _LINUX_JHASH_H */
diff --git a/src/xdp/include/linux/bpf.h b/src/xdp/include/linux/bpf.h
deleted file mode 100644
index 161a93809..000000000
--- a/src/xdp/include/linux/bpf.h
+++ /dev/null
@@ -1,3278 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- */
-#ifndef _UAPI__LINUX_BPF_H__
-#define _UAPI__LINUX_BPF_H__
-
-#include <linux/types.h>
-#include <linux/bpf_common.h>
-
-/* Extended instruction set based on top of classic BPF */
-
-/* instruction classes */
-#define BPF_JMP32 0x06 /* jmp mode in word width */
-#define BPF_ALU64 0x07 /* alu mode in double word width */
-
-/* ld/ldx fields */
-#define BPF_DW 0x18 /* double word (64-bit) */
-#define BPF_XADD 0xc0 /* exclusive add */
-
-/* alu/jmp fields */
-#define BPF_MOV 0xb0 /* mov reg to reg */
-#define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */
-
-/* change endianness of a register */
-#define BPF_END 0xd0 /* flags for endianness conversion: */
-#define BPF_TO_LE 0x00 /* convert to little-endian */
-#define BPF_TO_BE 0x08 /* convert to big-endian */
-#define BPF_FROM_LE BPF_TO_LE
-#define BPF_FROM_BE BPF_TO_BE
-
-/* jmp encodings */
-#define BPF_JNE 0x50 /* jump != */
-#define BPF_JLT 0xa0 /* LT is unsigned, '<' */
-#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */
-#define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */
-#define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */
-#define BPF_JSLT 0xc0 /* SLT is signed, '<' */
-#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */
-#define BPF_CALL 0x80 /* function call */
-#define BPF_EXIT 0x90 /* function return */
-
-/* Register numbers */
-enum {
- BPF_REG_0 = 0,
- BPF_REG_1,
- BPF_REG_2,
- BPF_REG_3,
- BPF_REG_4,
- BPF_REG_5,
- BPF_REG_6,
- BPF_REG_7,
- BPF_REG_8,
- BPF_REG_9,
- BPF_REG_10,
- __MAX_BPF_REG,
-};
-
-/* BPF has 10 general purpose 64-bit registers and stack frame. */
-#define MAX_BPF_REG __MAX_BPF_REG
-
-struct bpf_insn {
- __u8 code; /* opcode */
- __u8 dst_reg:4; /* dest register */
- __u8 src_reg:4; /* source register */
- __s16 off; /* signed offset */
- __s32 imm; /* signed immediate constant */
-};
-
-/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */
-struct bpf_lpm_trie_key {
- __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */
- __u8 data[0]; /* Arbitrary size */
-};
-
-struct bpf_cgroup_storage_key {
- __u64 cgroup_inode_id; /* cgroup inode id */
- __u32 attach_type; /* program attach type */
-};
-
-/* BPF syscall commands, see bpf(2) man-page for details. */
-enum bpf_cmd {
- BPF_MAP_CREATE,
- BPF_MAP_LOOKUP_ELEM,
- BPF_MAP_UPDATE_ELEM,
- BPF_MAP_DELETE_ELEM,
- BPF_MAP_GET_NEXT_KEY,
- BPF_PROG_LOAD,
- BPF_OBJ_PIN,
- BPF_OBJ_GET,
- BPF_PROG_ATTACH,
- BPF_PROG_DETACH,
- BPF_PROG_TEST_RUN,
- BPF_PROG_GET_NEXT_ID,
- BPF_MAP_GET_NEXT_ID,
- BPF_PROG_GET_FD_BY_ID,
- BPF_MAP_GET_FD_BY_ID,
- BPF_OBJ_GET_INFO_BY_FD,
- BPF_PROG_QUERY,
- BPF_RAW_TRACEPOINT_OPEN,
- BPF_BTF_LOAD,
- BPF_BTF_GET_FD_BY_ID,
- BPF_TASK_FD_QUERY,
- BPF_MAP_LOOKUP_AND_DELETE_ELEM,
-};
-
-enum bpf_map_type {
- BPF_MAP_TYPE_UNSPEC,
- BPF_MAP_TYPE_HASH,
- BPF_MAP_TYPE_ARRAY,
- BPF_MAP_TYPE_PROG_ARRAY,
- BPF_MAP_TYPE_PERF_EVENT_ARRAY,
- BPF_MAP_TYPE_PERCPU_HASH,
- BPF_MAP_TYPE_PERCPU_ARRAY,
- BPF_MAP_TYPE_STACK_TRACE,
- BPF_MAP_TYPE_CGROUP_ARRAY,
- BPF_MAP_TYPE_LRU_HASH,
- BPF_MAP_TYPE_LRU_PERCPU_HASH,
- BPF_MAP_TYPE_LPM_TRIE,
- BPF_MAP_TYPE_ARRAY_OF_MAPS,
- BPF_MAP_TYPE_HASH_OF_MAPS,
- BPF_MAP_TYPE_DEVMAP,
- BPF_MAP_TYPE_SOCKMAP,
- BPF_MAP_TYPE_CPUMAP,
- BPF_MAP_TYPE_XSKMAP,
- BPF_MAP_TYPE_SOCKHASH,
- BPF_MAP_TYPE_CGROUP_STORAGE,
- BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
- BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
- BPF_MAP_TYPE_QUEUE,
- BPF_MAP_TYPE_STACK,
-};
-
-/* Note that tracing related programs such as
- * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT}
- * are not subject to a stable API since kernel internal data
- * structures can change from release to release and may
- * therefore break existing tracing BPF programs. Tracing BPF
- * programs correspond to /a/ specific kernel which is to be
- * analyzed, and not /a/ specific kernel /and/ all future ones.
- */
-enum bpf_prog_type {
- BPF_PROG_TYPE_UNSPEC,
- BPF_PROG_TYPE_SOCKET_FILTER,
- BPF_PROG_TYPE_KPROBE,
- BPF_PROG_TYPE_SCHED_CLS,
- BPF_PROG_TYPE_SCHED_ACT,
- BPF_PROG_TYPE_TRACEPOINT,
- BPF_PROG_TYPE_XDP,
- BPF_PROG_TYPE_PERF_EVENT,
- BPF_PROG_TYPE_CGROUP_SKB,
- BPF_PROG_TYPE_CGROUP_SOCK,
- BPF_PROG_TYPE_LWT_IN,
- BPF_PROG_TYPE_LWT_OUT,
- BPF_PROG_TYPE_LWT_XMIT,
- BPF_PROG_TYPE_SOCK_OPS,
- BPF_PROG_TYPE_SK_SKB,
- BPF_PROG_TYPE_CGROUP_DEVICE,
- BPF_PROG_TYPE_SK_MSG,
- BPF_PROG_TYPE_RAW_TRACEPOINT,
- BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
- BPF_PROG_TYPE_LWT_SEG6LOCAL,
- BPF_PROG_TYPE_LIRC_MODE2,
- BPF_PROG_TYPE_SK_REUSEPORT,
- BPF_PROG_TYPE_FLOW_DISSECTOR,
-};
-
-enum bpf_attach_type {
- BPF_CGROUP_INET_INGRESS,
- BPF_CGROUP_INET_EGRESS,
- BPF_CGROUP_INET_SOCK_CREATE,
- BPF_CGROUP_SOCK_OPS,
- BPF_SK_SKB_STREAM_PARSER,
- BPF_SK_SKB_STREAM_VERDICT,
- BPF_CGROUP_DEVICE,
- BPF_SK_MSG_VERDICT,
- BPF_CGROUP_INET4_BIND,
- BPF_CGROUP_INET6_BIND,
- BPF_CGROUP_INET4_CONNECT,
- BPF_CGROUP_INET6_CONNECT,
- BPF_CGROUP_INET4_POST_BIND,
- BPF_CGROUP_INET6_POST_BIND,
- BPF_CGROUP_UDP4_SENDMSG,
- BPF_CGROUP_UDP6_SENDMSG,
- BPF_LIRC_MODE2,
- BPF_FLOW_DISSECTOR,
- __MAX_BPF_ATTACH_TYPE
-};
-
-#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
-
-/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
- *
- * NONE(default): No further bpf programs allowed in the subtree.
- *
- * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program,
- * the program in this cgroup yields to sub-cgroup program.
- *
- * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program,
- * that cgroup program gets run in addition to the program in this cgroup.
- *
- * Only one program is allowed to be attached to a cgroup with
- * NONE or BPF_F_ALLOW_OVERRIDE flag.
- * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will
- * release old program and attach the new one. Attach flags has to match.
- *
- * Multiple programs are allowed to be attached to a cgroup with
- * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order
- * (those that were attached first, run first)
- * The programs of sub-cgroup are executed first, then programs of
- * this cgroup and then programs of parent cgroup.
- * When children program makes decision (like picking TCP CA or sock bind)
- * parent program has a chance to override it.
- *
- * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups.
- * A cgroup with NONE doesn't allow any programs in sub-cgroups.
- * Ex1:
- * cgrp1 (MULTI progs A, B) ->
- * cgrp2 (OVERRIDE prog C) ->
- * cgrp3 (MULTI prog D) ->
- * cgrp4 (OVERRIDE prog E) ->
- * cgrp5 (NONE prog F)
- * the event in cgrp5 triggers execution of F,D,A,B in that order.
- * if prog F is detached, the execution is E,D,A,B
- * if prog F and D are detached, the execution is E,A,B
- * if prog F, E and D are detached, the execution is C,A,B
- *
- * All eligible programs are executed regardless of return code from
- * earlier programs.
- */
-#define BPF_F_ALLOW_OVERRIDE (1U << 0)
-#define BPF_F_ALLOW_MULTI (1U << 1)
-
-/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the
- * verifier will perform strict alignment checking as if the kernel
- * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set,
- * and NET_IP_ALIGN defined to 2.
- */
-#define BPF_F_STRICT_ALIGNMENT (1U << 0)
-
-/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the
- * verifier will allow any alignment whatsoever. On platforms
- * with strict alignment requirements for loads ands stores (such
- * as sparc and mips) the verifier validates that all loads and
- * stores provably follow this requirement. This flag turns that
- * checking and enforcement off.
- *
- * It is mostly used for testing when we want to validate the
- * context and memory access aspects of the verifier, but because
- * of an unaligned access the alignment check would trigger before
- * the one we are interested in.
- */
-#define BPF_F_ANY_ALIGNMENT (1U << 1)
-
-/* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */
-#define BPF_PSEUDO_MAP_FD 1
-
-/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
- * offset to another bpf function
- */
-#define BPF_PSEUDO_CALL 1
-
-/* flags for BPF_MAP_UPDATE_ELEM command */
-#define BPF_ANY 0 /* create new element or update existing */
-#define BPF_NOEXIST 1 /* create new element if it didn't exist */
-#define BPF_EXIST 2 /* update existing element */
-#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */
-
-/* flags for BPF_MAP_CREATE command */
-#define BPF_F_NO_PREALLOC (1U << 0)
-/* Instead of having one common LRU list in the
- * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list
- * which can scale and perform better.
- * Note, the LRU nodes (including free nodes) cannot be moved
- * across different LRU lists.
- */
-#define BPF_F_NO_COMMON_LRU (1U << 1)
-/* Specify numa node during map creation */
-#define BPF_F_NUMA_NODE (1U << 2)
-
-#define BPF_OBJ_NAME_LEN 16U
-
-/* Flags for accessing BPF object */
-#define BPF_F_RDONLY (1U << 3)
-#define BPF_F_WRONLY (1U << 4)
-
-/* Flag for stack_map, store build_id+offset instead of pointer */
-#define BPF_F_STACK_BUILD_ID (1U << 5)
-
-/* Zero-initialize hash function seed. This should only be used for testing. */
-#define BPF_F_ZERO_SEED (1U << 6)
-
-/* flags for BPF_PROG_QUERY */
-#define BPF_F_QUERY_EFFECTIVE (1U << 0)
-
-enum bpf_stack_build_id_status {
- /* user space need an empty entry to identify end of a trace */
- BPF_STACK_BUILD_ID_EMPTY = 0,
- /* with valid build_id and offset */
- BPF_STACK_BUILD_ID_VALID = 1,
- /* couldn't get build_id, fallback to ip */
- BPF_STACK_BUILD_ID_IP = 2,
-};
-
-#define BPF_BUILD_ID_SIZE 20
-struct bpf_stack_build_id {
- __s32 status;
- unsigned char build_id[BPF_BUILD_ID_SIZE];
- union {
- __u64 offset;
- __u64 ip;
- };
-};
-
-union bpf_attr {
- struct { /* anonymous struct used by BPF_MAP_CREATE command */
- __u32 map_type; /* one of enum bpf_map_type */
- __u32 key_size; /* size of key in bytes */
- __u32 value_size; /* size of value in bytes */
- __u32 max_entries; /* max number of entries in a map */
- __u32 map_flags; /* BPF_MAP_CREATE related
- * flags defined above.
- */
- __u32 inner_map_fd; /* fd pointing to the inner map */
- __u32 numa_node; /* numa node (effective only if
- * BPF_F_NUMA_NODE is set).
- */
- char map_name[BPF_OBJ_NAME_LEN];
- __u32 map_ifindex; /* ifindex of netdev to create on */
- __u32 btf_fd; /* fd pointing to a BTF type data */
- __u32 btf_key_type_id; /* BTF type_id of the key */
- __u32 btf_value_type_id; /* BTF type_id of the value */
- };
-
- struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
- __u32 map_fd;
- __aligned_u64 key;
- union {
- __aligned_u64 value;
- __aligned_u64 next_key;
- };
- __u64 flags;
- };
-
- struct { /* anonymous struct used by BPF_PROG_LOAD command */
- __u32 prog_type; /* one of enum bpf_prog_type */
- __u32 insn_cnt;
- __aligned_u64 insns;
- __aligned_u64 license;
- __u32 log_level; /* verbosity level of verifier */
- __u32 log_size; /* size of user buffer */
- __aligned_u64 log_buf; /* user supplied buffer */
- __u32 kern_version; /* not used */
- __u32 prog_flags;
- char prog_name[BPF_OBJ_NAME_LEN];
- __u32 prog_ifindex; /* ifindex of netdev to prep for */
- /* For some prog types expected attach type must be known at
- * load time to verify attach type specific parts of prog
- * (context accesses, allowed helpers, etc).
- */
- __u32 expected_attach_type;
- __u32 prog_btf_fd; /* fd pointing to BTF type data */
- __u32 func_info_rec_size; /* userspace bpf_func_info size */
- __aligned_u64 func_info; /* func info */
- __u32 func_info_cnt; /* number of bpf_func_info records */
- __u32 line_info_rec_size; /* userspace bpf_line_info size */
- __aligned_u64 line_info; /* line info */
- __u32 line_info_cnt; /* number of bpf_line_info records */
- };
-
- struct { /* anonymous struct used by BPF_OBJ_* commands */
- __aligned_u64 pathname;
- __u32 bpf_fd;
- __u32 file_flags;
- };
-
- struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
- __u32 target_fd; /* container object to attach to */
- __u32 attach_bpf_fd; /* eBPF program to attach */
- __u32 attach_type;
- __u32 attach_flags;
- };
-
- struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */
- __u32 prog_fd;
- __u32 retval;
- __u32 data_size_in; /* input: len of data_in */
- __u32 data_size_out; /* input/output: len of data_out
- * returns ENOSPC if data_out
- * is too small.
- */
- __aligned_u64 data_in;
- __aligned_u64 data_out;
- __u32 repeat;
- __u32 duration;
- } test;
-
- struct { /* anonymous struct used by BPF_*_GET_*_ID */
- union {
- __u32 start_id;
- __u32 prog_id;
- __u32 map_id;
- __u32 btf_id;
- };
- __u32 next_id;
- __u32 open_flags;
- };
-
- struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
- __u32 bpf_fd;
- __u32 info_len;
- __aligned_u64 info;
- } info;
-
- struct { /* anonymous struct used by BPF_PROG_QUERY command */
- __u32 target_fd; /* container object to query */
- __u32 attach_type;
- __u32 query_flags;
- __u32 attach_flags;
- __aligned_u64 prog_ids;
- __u32 prog_cnt;
- } query;
-
- struct {
- __u64 name;
- __u32 prog_fd;
- } raw_tracepoint;
-
- struct { /* anonymous struct for BPF_BTF_LOAD */
- __aligned_u64 btf;
- __aligned_u64 btf_log_buf;
- __u32 btf_size;
- __u32 btf_log_size;
- __u32 btf_log_level;
- };
-
- struct {
- __u32 pid; /* input: pid */
- __u32 fd; /* input: fd */
- __u32 flags; /* input: flags */
- __u32 buf_len; /* input/output: buf len */
- __aligned_u64 buf; /* input/output:
- * tp_name for tracepoint
- * symbol for kprobe
- * filename for uprobe
- */
- __u32 prog_id; /* output: prod_id */
- __u32 fd_type; /* output: BPF_FD_TYPE_* */
- __u64 probe_offset; /* output: probe_offset */
- __u64 probe_addr; /* output: probe_addr */
- } task_fd_query;
-} __attribute__((aligned(8)));
-
-/* The description below is an attempt at providing documentation to eBPF
- * developers about the multiple available eBPF helper functions. It can be
- * parsed and used to produce a manual page. The workflow is the following,
- * and requires the rst2man utility:
- *
- * $ ./scripts/bpf_helpers_doc.py \
- * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
- * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
- * $ man /tmp/bpf-helpers.7
- *
- * Note that in order to produce this external documentation, some RST
- * formatting is used in the descriptions to get "bold" and "italics" in
- * manual pages. Also note that the few trailing white spaces are
- * intentional, removing them would break paragraphs for rst2man.
- *
- * Start of BPF helper function descriptions:
- *
- * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
- * Description
- * Perform a lookup in *map* for an entry associated to *key*.
- * Return
- * Map value associated to *key*, or **NULL** if no entry was
- * found.
- *
- * int bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
- * Description
- * Add or update the value of the entry associated to *key* in
- * *map* with *value*. *flags* is one of:
- *
- * **BPF_NOEXIST**
- * The entry for *key* must not exist in the map.
- * **BPF_EXIST**
- * The entry for *key* must already exist in the map.
- * **BPF_ANY**
- * No condition on the existence of the entry for *key*.
- *
- * Flag value **BPF_NOEXIST** cannot be used for maps of types
- * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all
- * elements always exist), the helper would return an error.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_map_delete_elem(struct bpf_map *map, const void *key)
- * Description
- * Delete entry with *key* from *map*.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_probe_read(void *dst, u32 size, const void *src)
- * Description
- * For tracing programs, safely attempt to read *size* bytes from
- * address *src* and store the data in *dst*.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * u64 bpf_ktime_get_ns(void)
- * Description
- * Return the time elapsed since system boot, in nanoseconds.
- * Return
- * Current *ktime*.
- *
- * int bpf_trace_printk(const char *fmt, u32 fmt_size, ...)
- * Description
- * This helper is a "printk()-like" facility for debugging. It
- * prints a message defined by format *fmt* (of size *fmt_size*)
- * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if
- * available. It can take up to three additional **u64**
- * arguments (as an eBPF helpers, the total number of arguments is
- * limited to five).
- *
- * Each time the helper is called, it appends a line to the trace.
- * The format of the trace is customizable, and the exact output
- * one will get depends on the options set in
- * *\/sys/kernel/debug/tracing/trace_options* (see also the
- * *README* file under the same directory). However, it usually
- * defaults to something like:
- *
- * ::
- *
- * telnet-470 [001] .N.. 419421.045894: 0x00000001: <formatted msg>
- *
- * In the above:
- *
- * * ``telnet`` is the name of the current task.
- * * ``470`` is the PID of the current task.
- * * ``001`` is the CPU number on which the task is
- * running.
- * * In ``.N..``, each character refers to a set of
- * options (whether irqs are enabled, scheduling
- * options, whether hard/softirqs are running, level of
- * preempt_disabled respectively). **N** means that
- * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED**
- * are set.
- * * ``419421.045894`` is a timestamp.
- * * ``0x00000001`` is a fake value used by BPF for the
- * instruction pointer register.
- * * ``<formatted msg>`` is the message formatted with
- * *fmt*.
- *
- * The conversion specifiers supported by *fmt* are similar, but
- * more limited than for printk(). They are **%d**, **%i**,
- * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**,
- * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size
- * of field, padding with zeroes, etc.) is available, and the
- * helper will return **-EINVAL** (but print nothing) if it
- * encounters an unknown specifier.
- *
- * Also, note that **bpf_trace_printk**\ () is slow, and should
- * only be used for debugging purposes. For this reason, a notice
- * bloc (spanning several lines) is printed to kernel logs and
- * states that the helper should not be used "for production use"
- * the first time this helper is used (or more precisely, when
- * **trace_printk**\ () buffers are allocated). For passing values
- * to user space, perf events should be preferred.
- * Return
- * The number of bytes written to the buffer, or a negative error
- * in case of failure.
- *
- * u32 bpf_get_prandom_u32(void)
- * Description
- * Get a pseudo-random number.
- *
- * From a security point of view, this helper uses its own
- * pseudo-random internal state, and cannot be used to infer the
- * seed of other random functions in the kernel. However, it is
- * essential to note that the generator used by the helper is not
- * cryptographically secure.
- * Return
- * A random 32-bit unsigned value.
- *
- * u32 bpf_get_smp_processor_id(void)
- * Description
- * Get the SMP (symmetric multiprocessing) processor id. Note that
- * all programs run with preemption disabled, which means that the
- * SMP processor id is stable during all the execution of the
- * program.
- * Return
- * The SMP id of the processor running the program.
- *
- * int bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags)
- * Description
- * Store *len* bytes from address *from* into the packet
- * associated to *skb*, at *offset*. *flags* are a combination of
- * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the
- * checksum for the packet after storing the bytes) and
- * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\
- * **->swhash** and *skb*\ **->l4hash** to 0).
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size)
- * Description
- * Recompute the layer 3 (e.g. IP) checksum for the packet
- * associated to *skb*. Computation is incremental, so the helper
- * must know the former value of the header field that was
- * modified (*from*), the new value of this field (*to*), and the
- * number of bytes (2 or 4) for this field, stored in *size*.
- * Alternatively, it is possible to store the difference between
- * the previous and the new values of the header field in *to*, by
- * setting *from* and *size* to 0. For both methods, *offset*
- * indicates the location of the IP checksum within the packet.
- *
- * This helper works in combination with **bpf_csum_diff**\ (),
- * which does not update the checksum in-place, but offers more
- * flexibility and can handle sizes larger than 2 or 4 for the
- * checksum to update.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags)
- * Description
- * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the
- * packet associated to *skb*. Computation is incremental, so the
- * helper must know the former value of the header field that was
- * modified (*from*), the new value of this field (*to*), and the
- * number of bytes (2 or 4) for this field, stored on the lowest
- * four bits of *flags*. Alternatively, it is possible to store
- * the difference between the previous and the new values of the
- * header field in *to*, by setting *from* and the four lowest
- * bits of *flags* to 0. For both methods, *offset* indicates the
- * location of the IP checksum within the packet. In addition to
- * the size of the field, *flags* can be added (bitwise OR) actual
- * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left
- * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
- * for updates resulting in a null checksum the value is set to
- * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
- * the checksum is to be computed against a pseudo-header.
- *
- * This helper works in combination with **bpf_csum_diff**\ (),
- * which does not update the checksum in-place, but offers more
- * flexibility and can handle sizes larger than 2 or 4 for the
- * checksum to update.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index)
- * Description
- * This special helper is used to trigger a "tail call", or in
- * other words, to jump into another eBPF program. The same stack
- * frame is used (but values on stack and in registers for the
- * caller are not accessible to the callee). This mechanism allows
- * for program chaining, either for raising the maximum number of
- * available eBPF instructions, or to execute given programs in
- * conditional blocks. For security reasons, there is an upper
- * limit to the number of successive tail calls that can be
- * performed.
- *
- * Upon call of this helper, the program attempts to jump into a
- * program referenced at index *index* in *prog_array_map*, a
- * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes
- * *ctx*, a pointer to the context.
- *
- * If the call succeeds, the kernel immediately runs the first
- * instruction of the new program. This is not a function call,
- * and it never returns to the previous program. If the call
- * fails, then the helper has no effect, and the caller continues
- * to run its subsequent instructions. A call can fail if the
- * destination program for the jump does not exist (i.e. *index*
- * is superior to the number of entries in *prog_array_map*), or
- * if the maximum number of tail calls has been reached for this
- * chain of programs. This limit is defined in the kernel by the
- * macro **MAX_TAIL_CALL_CNT** (not accessible to user space),
- * which is currently set to 32.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags)
- * Description
- * Clone and redirect the packet associated to *skb* to another
- * net device of index *ifindex*. Both ingress and egress
- * interfaces can be used for redirection. The **BPF_F_INGRESS**
- * value in *flags* is used to make the distinction (ingress path
- * is selected if the flag is present, egress path otherwise).
- * This is the only flag supported for now.
- *
- * In comparison with **bpf_redirect**\ () helper,
- * **bpf_clone_redirect**\ () has the associated cost of
- * duplicating the packet buffer, but this can be executed out of
- * the eBPF program. Conversely, **bpf_redirect**\ () is more
- * efficient, but it is handled through an action code where the
- * redirection happens only after the eBPF program has returned.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * u64 bpf_get_current_pid_tgid(void)
- * Return
- * A 64-bit integer containing the current tgid and pid, and
- * created as such:
- * *current_task*\ **->tgid << 32 \|**
- * *current_task*\ **->pid**.
- *
- * u64 bpf_get_current_uid_gid(void)
- * Return
- * A 64-bit integer containing the current GID and UID, and
- * created as such: *current_gid* **<< 32 \|** *current_uid*.
- *
- * int bpf_get_current_comm(char *buf, u32 size_of_buf)
- * Description
- * Copy the **comm** attribute of the current task into *buf* of
- * *size_of_buf*. The **comm** attribute contains the name of
- * the executable (excluding the path) for the current task. The
- * *size_of_buf* must be strictly positive. On success, the
- * helper makes sure that the *buf* is NUL-terminated. On failure,
- * it is filled with zeroes.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * u32 bpf_get_cgroup_classid(struct sk_buff *skb)
- * Description
- * Retrieve the classid for the current task, i.e. for the net_cls
- * cgroup to which *skb* belongs.
- *
- * This helper can be used on TC egress path, but not on ingress.
- *
- * The net_cls cgroup provides an interface to tag network packets
- * based on a user-provided identifier for all traffic coming from
- * the tasks belonging to the related cgroup. See also the related
- * kernel documentation, available from the Linux sources in file
- * *Documentation/cgroup-v1/net_cls.txt*.
- *
- * The Linux kernel has two versions for cgroups: there are
- * cgroups v1 and cgroups v2. Both are available to users, who can
- * use a mixture of them, but note that the net_cls cgroup is for
- * cgroup v1 only. This makes it incompatible with BPF programs
- * run on cgroups, which is a cgroup-v2-only feature (a socket can
- * only hold data for one version of cgroups at a time).
- *
- * This helper is only available is the kernel was compiled with
- * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to
- * "**y**" or to "**m**".
- * Return
- * The classid, or 0 for the default unconfigured classid.
- *
- * int bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
- * Description
- * Push a *vlan_tci* (VLAN tag control information) of protocol
- * *vlan_proto* to the packet associated to *skb*, then update
- * the checksum. Note that if *vlan_proto* is different from
- * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to
- * be **ETH_P_8021Q**.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_vlan_pop(struct sk_buff *skb)
- * Description
- * Pop a VLAN header from the packet associated to *skb*.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags)
- * Description
- * Get tunnel metadata. This helper takes a pointer *key* to an
- * empty **struct bpf_tunnel_key** of **size**, that will be
- * filled with tunnel metadata for the packet associated to *skb*.
- * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which
- * indicates that the tunnel is based on IPv6 protocol instead of
- * IPv4.
- *
- * The **struct bpf_tunnel_key** is an object that generalizes the
- * principal parameters used by various tunneling protocols into a
- * single struct. This way, it can be used to easily make a
- * decision based on the contents of the encapsulation header,
- * "summarized" in this struct. In particular, it holds the IP
- * address of the remote end (IPv4 or IPv6, depending on the case)
- * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also,
- * this struct exposes the *key*\ **->tunnel_id**, which is
- * generally mapped to a VNI (Virtual Network Identifier), making
- * it programmable together with the **bpf_skb_set_tunnel_key**\
- * () helper.
- *
- * Let's imagine that the following code is part of a program
- * attached to the TC ingress interface, on one end of a GRE
- * tunnel, and is supposed to filter out all messages coming from
- * remote ends with IPv4 address other than 10.0.0.1:
- *
- * ::
- *
- * int ret;
- * struct bpf_tunnel_key key = {};
- *
- * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
- * if (ret < 0)
- * return TC_ACT_SHOT; // drop packet
- *
- * if (key.remote_ipv4 != 0x0a000001)
- * return TC_ACT_SHOT; // drop packet
- *
- * return TC_ACT_OK; // accept packet
- *
- * This interface can also be used with all encapsulation devices
- * that can operate in "collect metadata" mode: instead of having
- * one network device per specific configuration, the "collect
- * metadata" mode only requires a single device where the
- * configuration can be extracted from this helper.
- *
- * This can be used together with various tunnels such as VXLan,
- * Geneve, GRE or IP in IP (IPIP).
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags)
- * Description
- * Populate tunnel metadata for packet associated to *skb.* The
- * tunnel metadata is set to the contents of *key*, of *size*. The
- * *flags* can be set to a combination of the following values:
- *
- * **BPF_F_TUNINFO_IPV6**
- * Indicate that the tunnel is based on IPv6 protocol
- * instead of IPv4.
- * **BPF_F_ZERO_CSUM_TX**
- * For IPv4 packets, add a flag to tunnel metadata
- * indicating that checksum computation should be skipped
- * and checksum set to zeroes.
- * **BPF_F_DONT_FRAGMENT**
- * Add a flag to tunnel metadata indicating that the
- * packet should not be fragmented.
- * **BPF_F_SEQ_NUMBER**
- * Add a flag to tunnel metadata indicating that a
- * sequence number should be added to tunnel header before
- * sending the packet. This flag was added for GRE
- * encapsulation, but might be used with other protocols
- * as well in the future.
- *
- * Here is a typical usage on the transmit path:
- *
- * ::
- *
- * struct bpf_tunnel_key key;
- * populate key ...
- * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
- * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0);
- *
- * See also the description of the **bpf_skb_get_tunnel_key**\ ()
- * helper for additional information.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags)
- * Description
- * Read the value of a perf event counter. This helper relies on a
- * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of
- * the perf event counter is selected when *map* is updated with
- * perf event file descriptors. The *map* is an array whose size
- * is the number of available CPUs, and each cell contains a value
- * relative to one CPU. The value to retrieve is indicated by
- * *flags*, that contains the index of the CPU to look up, masked
- * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
- * **BPF_F_CURRENT_CPU** to indicate that the value for the
- * current CPU should be retrieved.
- *
- * Note that before Linux 4.13, only hardware perf event can be
- * retrieved.
- *
- * Also, be aware that the newer helper
- * **bpf_perf_event_read_value**\ () is recommended over
- * **bpf_perf_event_read**\ () in general. The latter has some ABI
- * quirks where error and counter value are used as a return code
- * (which is wrong to do since ranges may overlap). This issue is
- * fixed with **bpf_perf_event_read_value**\ (), which at the same
- * time provides more features over the **bpf_perf_event_read**\
- * () interface. Please refer to the description of
- * **bpf_perf_event_read_value**\ () for details.
- * Return
- * The value of the perf event counter read from the map, or a
- * negative error code in case of failure.
- *
- * int bpf_redirect(u32 ifindex, u64 flags)
- * Description
- * Redirect the packet to another net device of index *ifindex*.
- * This helper is somewhat similar to **bpf_clone_redirect**\
- * (), except that the packet is not cloned, which provides
- * increased performance.
- *
- * Except for XDP, both ingress and egress interfaces can be used
- * for redirection. The **BPF_F_INGRESS** value in *flags* is used
- * to make the distinction (ingress path is selected if the flag
- * is present, egress path otherwise). Currently, XDP only
- * supports redirection to the egress interface, and accepts no
- * flag at all.
- *
- * The same effect can be attained with the more generic
- * **bpf_redirect_map**\ (), which requires specific maps to be
- * used but offers better performance.
- * Return
- * For XDP, the helper returns **XDP_REDIRECT** on success or
- * **XDP_ABORTED** on error. For other program types, the values
- * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on
- * error.
- *
- * u32 bpf_get_route_realm(struct sk_buff *skb)
- * Description
- * Retrieve the realm or the route, that is to say the
- * **tclassid** field of the destination for the *skb*. The
- * indentifier retrieved is a user-provided tag, similar to the
- * one used with the net_cls cgroup (see description for
- * **bpf_get_cgroup_classid**\ () helper), but here this tag is
- * held by a route (a destination entry), not by a task.
- *
- * Retrieving this identifier works with the clsact TC egress hook
- * (see also **tc-bpf(8)**), or alternatively on conventional
- * classful egress qdiscs, but not on TC ingress path. In case of
- * clsact TC egress hook, this has the advantage that, internally,
- * the destination entry has not been dropped yet in the transmit
- * path. Therefore, the destination entry does not need to be
- * artificially held via **netif_keep_dst**\ () for a classful
- * qdisc until the *skb* is freed.
- *
- * This helper is available only if the kernel was compiled with
- * **CONFIG_IP_ROUTE_CLASSID** configuration option.
- * Return
- * The realm of the route for the packet associated to *skb*, or 0
- * if none was found.
- *
- * int bpf_perf_event_output(struct pt_reg *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
- * Description
- * Write raw *data* blob into a special BPF perf event held by
- * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
- * event must have the following attributes: **PERF_SAMPLE_RAW**
- * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and
- * **PERF_COUNT_SW_BPF_OUTPUT** as **config**.
- *
- * The *flags* are used to indicate the index in *map* for which
- * the value must be put, masked with **BPF_F_INDEX_MASK**.
- * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU**
- * to indicate that the index of the current CPU core should be
- * used.
- *
- * The value to write, of *size*, is passed through eBPF stack and
- * pointed by *data*.
- *
- * The context of the program *ctx* needs also be passed to the
- * helper.
- *
- * On user space, a program willing to read the values needs to
- * call **perf_event_open**\ () on the perf event (either for
- * one or for all CPUs) and to store the file descriptor into the
- * *map*. This must be done before the eBPF program can send data
- * into it. An example is available in file
- * *samples/bpf/trace_output_user.c* in the Linux kernel source
- * tree (the eBPF program counterpart is in
- * *samples/bpf/trace_output_kern.c*).
- *
- * **bpf_perf_event_output**\ () achieves better performance
- * than **bpf_trace_printk**\ () for sharing data with user
- * space, and is much better suitable for streaming data from eBPF
- * programs.
- *
- * Note that this helper is not restricted to tracing use cases
- * and can be used with programs attached to TC or XDP as well,
- * where it allows for passing data to user space listeners. Data
- * can be:
- *
- * * Only custom structs,
- * * Only the packet payload, or
- * * A combination of both.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
- * Description
- * This helper was provided as an easy way to load data from a
- * packet. It can be used to load *len* bytes from *offset* from
- * the packet associated to *skb*, into the buffer pointed by
- * *to*.
- *
- * Since Linux 4.7, usage of this helper has mostly been replaced
- * by "direct packet access", enabling packet data to be
- * manipulated with *skb*\ **->data** and *skb*\ **->data_end**
- * pointing respectively to the first byte of packet data and to
- * the byte after the last byte of packet data. However, it
- * remains useful if one wishes to read large quantities of data
- * at once from a packet into the eBPF stack.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_get_stackid(struct pt_reg *ctx, struct bpf_map *map, u64 flags)
- * Description
- * Walk a user or a kernel stack and return its id. To achieve
- * this, the helper needs *ctx*, which is a pointer to the context
- * on which the tracing program is executed, and a pointer to a
- * *map* of type **BPF_MAP_TYPE_STACK_TRACE**.
- *
- * The last argument, *flags*, holds the number of stack frames to
- * skip (from 0 to 255), masked with
- * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
- * a combination of the following flags:
- *
- * **BPF_F_USER_STACK**
- * Collect a user space stack instead of a kernel stack.
- * **BPF_F_FAST_STACK_CMP**
- * Compare stacks by hash only.
- * **BPF_F_REUSE_STACKID**
- * If two different stacks hash into the same *stackid*,
- * discard the old one.
- *
- * The stack id retrieved is a 32 bit long integer handle which
- * can be further combined with other data (including other stack
- * ids) and used as a key into maps. This can be useful for
- * generating a variety of graphs (such as flame graphs or off-cpu
- * graphs).
- *
- * For walking a stack, this helper is an improvement over
- * **bpf_probe_read**\ (), which can be used with unrolled loops
- * but is not efficient and consumes a lot of eBPF instructions.
- * Instead, **bpf_get_stackid**\ () can collect up to
- * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that
- * this limit can be controlled with the **sysctl** program, and
- * that it should be manually increased in order to profile long
- * user stacks (such as stacks for Java programs). To do so, use:
- *
- * ::
- *
- * # sysctl kernel.perf_event_max_stack=<new value>
- * Return
- * The positive or null stack id on success, or a negative error
- * in case of failure.
- *
- * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed)
- * Description
- * Compute a checksum difference, from the raw buffer pointed by
- * *from*, of length *from_size* (that must be a multiple of 4),
- * towards the raw buffer pointed by *to*, of size *to_size*
- * (same remark). An optional *seed* can be added to the value
- * (this can be cascaded, the seed may come from a previous call
- * to the helper).
- *
- * This is flexible enough to be used in several ways:
- *
- * * With *from_size* == 0, *to_size* > 0 and *seed* set to
- * checksum, it can be used when pushing new data.
- * * With *from_size* > 0, *to_size* == 0 and *seed* set to
- * checksum, it can be used when removing data from a packet.
- * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it
- * can be used to compute a diff. Note that *from_size* and
- * *to_size* do not need to be equal.
- *
- * This helper can be used in combination with
- * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to
- * which one can feed in the difference computed with
- * **bpf_csum_diff**\ ().
- * Return
- * The checksum result, or a negative error code in case of
- * failure.
- *
- * int bpf_skb_get_tunnel_opt(struct sk_buff *skb, u8 *opt, u32 size)
- * Description
- * Retrieve tunnel options metadata for the packet associated to
- * *skb*, and store the raw tunnel option data to the buffer *opt*
- * of *size*.
- *
- * This helper can be used with encapsulation devices that can
- * operate in "collect metadata" mode (please refer to the related
- * note in the description of **bpf_skb_get_tunnel_key**\ () for
- * more details). A particular example where this can be used is
- * in combination with the Geneve encapsulation protocol, where it
- * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper)
- * and retrieving arbitrary TLVs (Type-Length-Value headers) from
- * the eBPF program. This allows for full customization of these
- * headers.
- * Return
- * The size of the option data retrieved.
- *
- * int bpf_skb_set_tunnel_opt(struct sk_buff *skb, u8 *opt, u32 size)
- * Description
- * Set tunnel options metadata for the packet associated to *skb*
- * to the option data contained in the raw buffer *opt* of *size*.
- *
- * See also the description of the **bpf_skb_get_tunnel_opt**\ ()
- * helper for additional information.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags)
- * Description
- * Change the protocol of the *skb* to *proto*. Currently
- * supported are transition from IPv4 to IPv6, and from IPv6 to
- * IPv4. The helper takes care of the groundwork for the
- * transition, including resizing the socket buffer. The eBPF
- * program is expected to fill the new headers, if any, via
- * **skb_store_bytes**\ () and to recompute the checksums with
- * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\
- * (). The main case for this helper is to perform NAT64
- * operations out of an eBPF program.
- *
- * Internally, the GSO type is marked as dodgy so that headers are
- * checked and segments are recalculated by the GSO/GRO engine.
- * The size for GSO target is adapted as well.
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_change_type(struct sk_buff *skb, u32 type)
- * Description
- * Change the packet type for the packet associated to *skb*. This
- * comes down to setting *skb*\ **->pkt_type** to *type*, except
- * the eBPF program does not have a write access to *skb*\
- * **->pkt_type** beside this helper. Using a helper here allows
- * for graceful handling of errors.
- *
- * The major use case is to change incoming *skb*s to
- * **PACKET_HOST** in a programmatic way instead of having to
- * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for
- * example.
- *
- * Note that *type* only allows certain values. At this time, they
- * are:
- *
- * **PACKET_HOST**
- * Packet is for us.
- * **PACKET_BROADCAST**
- * Send packet to all.
- * **PACKET_MULTICAST**
- * Send packet to group.
- * **PACKET_OTHERHOST**
- * Send packet to someone else.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index)
- * Description
- * Check whether *skb* is a descendant of the cgroup2 held by
- * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
- * Return
- * The return value depends on the result of the test, and can be:
- *
- * * 0, if the *skb* failed the cgroup2 descendant test.
- * * 1, if the *skb* succeeded the cgroup2 descendant test.
- * * A negative error code, if an error occurred.
- *
- * u32 bpf_get_hash_recalc(struct sk_buff *skb)
- * Description
- * Retrieve the hash of the packet, *skb*\ **->hash**. If it is
- * not set, in particular if the hash was cleared due to mangling,
- * recompute this hash. Later accesses to the hash can be done
- * directly with *skb*\ **->hash**.
- *
- * Calling **bpf_set_hash_invalid**\ (), changing a packet
- * prototype with **bpf_skb_change_proto**\ (), or calling
- * **bpf_skb_store_bytes**\ () with the
- * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear
- * the hash and to trigger a new computation for the next call to
- * **bpf_get_hash_recalc**\ ().
- * Return
- * The 32-bit hash.
- *
- * u64 bpf_get_current_task(void)
- * Return
- * A pointer to the current task struct.
- *
- * int bpf_probe_write_user(void *dst, const void *src, u32 len)
- * Description
- * Attempt in a safe way to write *len* bytes from the buffer
- * *src* to *dst* in memory. It only works for threads that are in
- * user context, and *dst* must be a valid user space address.
- *
- * This helper should not be used to implement any kind of
- * security mechanism because of TOC-TOU attacks, but rather to
- * debug, divert, and manipulate execution of semi-cooperative
- * processes.
- *
- * Keep in mind that this feature is meant for experiments, and it
- * has a risk of crashing the system and running programs.
- * Therefore, when an eBPF program using this helper is attached,
- * a warning including PID and process name is printed to kernel
- * logs.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_current_task_under_cgroup(struct bpf_map *map, u32 index)
- * Description
- * Check whether the probe is being run is the context of a given
- * subset of the cgroup2 hierarchy. The cgroup2 to test is held by
- * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
- * Return
- * The return value depends on the result of the test, and can be:
- *
- * * 0, if the *skb* task belongs to the cgroup2.
- * * 1, if the *skb* task does not belong to the cgroup2.
- * * A negative error code, if an error occurred.
- *
- * int bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags)
- * Description
- * Resize (trim or grow) the packet associated to *skb* to the
- * new *len*. The *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * The basic idea is that the helper performs the needed work to
- * change the size of the packet, then the eBPF program rewrites
- * the rest via helpers like **bpf_skb_store_bytes**\ (),
- * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ ()
- * and others. This helper is a slow path utility intended for
- * replies with control messages. And because it is targeted for
- * slow path, the helper itself can afford to be slow: it
- * implicitly linearizes, unclones and drops offloads from the
- * *skb*.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_pull_data(struct sk_buff *skb, u32 len)
- * Description
- * Pull in non-linear data in case the *skb* is non-linear and not
- * all of *len* are part of the linear section. Make *len* bytes
- * from *skb* readable and writable. If a zero value is passed for
- * *len*, then the whole length of the *skb* is pulled.
- *
- * This helper is only needed for reading and writing with direct
- * packet access.
- *
- * For direct packet access, testing that offsets to access
- * are within packet boundaries (test on *skb*\ **->data_end**) is
- * susceptible to fail if offsets are invalid, or if the requested
- * data is in non-linear parts of the *skb*. On failure the
- * program can just bail out, or in the case of a non-linear
- * buffer, use a helper to make the data available. The
- * **bpf_skb_load_bytes**\ () helper is a first solution to access
- * the data. Another one consists in using **bpf_skb_pull_data**
- * to pull in once the non-linear parts, then retesting and
- * eventually access the data.
- *
- * At the same time, this also makes sure the *skb* is uncloned,
- * which is a necessary condition for direct write. As this needs
- * to be an invariant for the write part only, the verifier
- * detects writes and adds a prologue that is calling
- * **bpf_skb_pull_data()** to effectively unclone the *skb* from
- * the very beginning in case it is indeed cloned.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum)
- * Description
- * Add the checksum *csum* into *skb*\ **->csum** in case the
- * driver has supplied a checksum for the entire packet into that
- * field. Return an error otherwise. This helper is intended to be
- * used in combination with **bpf_csum_diff**\ (), in particular
- * when the checksum needs to be updated after data has been
- * written into the packet through direct packet access.
- * Return
- * The checksum on success, or a negative error code in case of
- * failure.
- *
- * void bpf_set_hash_invalid(struct sk_buff *skb)
- * Description
- * Invalidate the current *skb*\ **->hash**. It can be used after
- * mangling on headers through direct packet access, in order to
- * indicate that the hash is outdated and to trigger a
- * recalculation the next time the kernel tries to access this
- * hash or when the **bpf_get_hash_recalc**\ () helper is called.
- *
- * int bpf_get_numa_node_id(void)
- * Description
- * Return the id of the current NUMA node. The primary use case
- * for this helper is the selection of sockets for the local NUMA
- * node, when the program is attached to sockets using the
- * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**),
- * but the helper is also available to other eBPF program types,
- * similarly to **bpf_get_smp_processor_id**\ ().
- * Return
- * The id of current NUMA node.
- *
- * int bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags)
- * Description
- * Grows headroom of packet associated to *skb* and adjusts the
- * offset of the MAC header accordingly, adding *len* bytes of
- * space. It automatically extends and reallocates memory as
- * required.
- *
- * This helper can be used on a layer 3 *skb* to push a MAC header
- * for redirection into a layer 2 device.
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta)
- * Description
- * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
- * it is possible to use a negative value for *delta*. This helper
- * can be used to prepare the packet for pushing or popping
- * headers.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
- * Description
- * Copy a NUL terminated string from an unsafe address
- * *unsafe_ptr* to *dst*. The *size* should include the
- * terminating NUL byte. In case the string length is smaller than
- * *size*, the target is not padded with further NUL bytes. If the
- * string length is larger than *size*, just *size*-1 bytes are
- * copied and the last byte is set to NUL.
- *
- * On success, the length of the copied string is returned. This
- * makes this helper useful in tracing programs for reading
- * strings, and more importantly to get its length at runtime. See
- * the following snippet:
- *
- * ::
- *
- * SEC("kprobe/sys_open")
- * void bpf_sys_open(struct pt_regs *ctx)
- * {
- * char buf[PATHLEN]; // PATHLEN is defined to 256
- * int res = bpf_probe_read_str(buf, sizeof(buf),
- * ctx->di);
- *
- * // Consume buf, for example push it to
- * // userspace via bpf_perf_event_output(); we
- * // can use res (the string length) as event
- * // size, after checking its boundaries.
- * }
- *
- * In comparison, using **bpf_probe_read()** helper here instead
- * to read the string would require to estimate the length at
- * compile time, and would often result in copying more memory
- * than necessary.
- *
- * Another useful use case is when parsing individual process
- * arguments or individual environment variables navigating
- * *current*\ **->mm->arg_start** and *current*\
- * **->mm->env_start**: using this helper and the return value,
- * one can quickly iterate at the right offset of the memory area.
- * Return
- * On success, the strictly positive length of the string,
- * including the trailing NUL character. On error, a negative
- * value.
- *
- * u64 bpf_get_socket_cookie(struct sk_buff *skb)
- * Description
- * If the **struct sk_buff** pointed by *skb* has a known socket,
- * retrieve the cookie (generated by the kernel) of this socket.
- * If no cookie has been set yet, generate a new cookie. Once
- * generated, the socket cookie remains stable for the life of the
- * socket. This helper can be useful for monitoring per socket
- * networking traffic statistics as it provides a unique socket
- * identifier per namespace.
- * Return
- * A 8-byte long non-decreasing number on success, or 0 if the
- * socket field is missing inside *skb*.
- *
- * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx)
- * Description
- * Equivalent to bpf_get_socket_cookie() helper that accepts
- * *skb*, but gets socket from **struct bpf_sock_addr** context.
- * Return
- * A 8-byte long non-decreasing number.
- *
- * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx)
- * Description
- * Equivalent to bpf_get_socket_cookie() helper that accepts
- * *skb*, but gets socket from **struct bpf_sock_ops** context.
- * Return
- * A 8-byte long non-decreasing number.
- *
- * u32 bpf_get_socket_uid(struct sk_buff *skb)
- * Return
- * The owner UID of the socket associated to *skb*. If the socket
- * is **NULL**, or if it is not a full socket (i.e. if it is a
- * time-wait or a request socket instead), **overflowuid** value
- * is returned (note that **overflowuid** might also be the actual
- * UID value for the socket).
- *
- * u32 bpf_set_hash(struct sk_buff *skb, u32 hash)
- * Description
- * Set the full hash for *skb* (set the field *skb*\ **->hash**)
- * to value *hash*.
- * Return
- * 0
- *
- * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *optval, int optlen)
- * Description
- * Emulate a call to **setsockopt()** on the socket associated to
- * *bpf_socket*, which must be a full socket. The *level* at
- * which the option resides and the name *optname* of the option
- * must be specified, see **setsockopt(2)** for more information.
- * The option value of length *optlen* is pointed by *optval*.
- *
- * This helper actually implements a subset of **setsockopt()**.
- * It supports the following *level*\ s:
- *
- * * **SOL_SOCKET**, which supports the following *optname*\ s:
- * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**,
- * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**.
- * * **IPPROTO_TCP**, which supports the following *optname*\ s:
- * **TCP_CONGESTION**, **TCP_BPF_IW**,
- * **TCP_BPF_SNDCWND_CLAMP**.
- * * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
- * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_adjust_room(struct sk_buff *skb, s32 len_diff, u32 mode, u64 flags)
- * Description
- * Grow or shrink the room for data in the packet associated to
- * *skb* by *len_diff*, and according to the selected *mode*.
- *
- * There are two supported modes at this time:
- *
- * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer
- * (room space is added or removed below the layer 2 header).
- *
- * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
- * (room space is added or removed below the layer 3 header).
- *
- * The following flags are supported at this time:
- *
- * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size.
- * Adjusting mss in this way is not allowed for datagrams.
- *
- * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 **:
- * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 **:
- * Any new space is reserved to hold a tunnel header.
- * Configure skb offsets and other fields accordingly.
- *
- * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE **:
- * * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP **:
- * Use with ENCAP_L3 flags to further specify the tunnel type.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags)
- * Description
- * Redirect the packet to the endpoint referenced by *map* at
- * index *key*. Depending on its type, this *map* can contain
- * references to net devices (for forwarding packets through other
- * ports), or to CPUs (for redirecting XDP frames to another CPU;
- * but this is only implemented for native XDP (with driver
- * support) as of this writing).
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * When used to redirect packets to net devices, this helper
- * provides a high performance increase over **bpf_redirect**\ ().
- * This is due to various implementation details of the underlying
- * mechanisms, one of which is the fact that **bpf_redirect_map**\
- * () tries to send packet as a "bulk" to the device.
- * Return
- * **XDP_REDIRECT** on success, or **XDP_ABORTED** on error.
- *
- * int bpf_sk_redirect_map(struct bpf_map *map, u32 key, u64 flags)
- * Description
- * Redirect the packet to the socket referenced by *map* (of type
- * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
- * egress interfaces can be used for redirection. The
- * **BPF_F_INGRESS** value in *flags* is used to make the
- * distinction (ingress path is selected if the flag is present,
- * egress path otherwise). This is the only flag supported for now.
- * Return
- * **SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
- * Description
- * Add an entry to, or update a *map* referencing sockets. The
- * *skops* is used as a new value for the entry associated to
- * *key*. *flags* is one of:
- *
- * **BPF_NOEXIST**
- * The entry for *key* must not exist in the map.
- * **BPF_EXIST**
- * The entry for *key* must already exist in the map.
- * **BPF_ANY**
- * No condition on the existence of the entry for *key*.
- *
- * If the *map* has eBPF programs (parser and verdict), those will
- * be inherited by the socket being added. If the socket is
- * already attached to eBPF programs, this results in an error.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta)
- * Description
- * Adjust the address pointed by *xdp_md*\ **->data_meta** by
- * *delta* (which can be positive or negative). Note that this
- * operation modifies the address stored in *xdp_md*\ **->data**,
- * so the latter must be loaded only after the helper has been
- * called.
- *
- * The use of *xdp_md*\ **->data_meta** is optional and programs
- * are not required to use it. The rationale is that when the
- * packet is processed with XDP (e.g. as DoS filter), it is
- * possible to push further meta data along with it before passing
- * to the stack, and to give the guarantee that an ingress eBPF
- * program attached as a TC classifier on the same device can pick
- * this up for further post-processing. Since TC works with socket
- * buffers, it remains possible to set from XDP the **mark** or
- * **priority** pointers, or other pointers for the socket buffer.
- * Having this scratch space generic and programmable allows for
- * more flexibility as the user is free to store whatever meta
- * data they need.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size)
- * Description
- * Read the value of a perf event counter, and store it into *buf*
- * of size *buf_size*. This helper relies on a *map* of type
- * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event
- * counter is selected when *map* is updated with perf event file
- * descriptors. The *map* is an array whose size is the number of
- * available CPUs, and each cell contains a value relative to one
- * CPU. The value to retrieve is indicated by *flags*, that
- * contains the index of the CPU to look up, masked with
- * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
- * **BPF_F_CURRENT_CPU** to indicate that the value for the
- * current CPU should be retrieved.
- *
- * This helper behaves in a way close to
- * **bpf_perf_event_read**\ () helper, save that instead of
- * just returning the value observed, it fills the *buf*
- * structure. This allows for additional data to be retrieved: in
- * particular, the enabled and running times (in *buf*\
- * **->enabled** and *buf*\ **->running**, respectively) are
- * copied. In general, **bpf_perf_event_read_value**\ () is
- * recommended over **bpf_perf_event_read**\ (), which has some
- * ABI issues and provides fewer functionalities.
- *
- * These values are interesting, because hardware PMU (Performance
- * Monitoring Unit) counters are limited resources. When there are
- * more PMU based perf events opened than available counters,
- * kernel will multiplex these events so each event gets certain
- * percentage (but not all) of the PMU time. In case that
- * multiplexing happens, the number of samples or counter value
- * will not reflect the case compared to when no multiplexing
- * occurs. This makes comparison between different runs difficult.
- * Typically, the counter value should be normalized before
- * comparing to other experiments. The usual normalization is done
- * as follows.
- *
- * ::
- *
- * normalized_counter = counter * t_enabled / t_running
- *
- * Where t_enabled is the time enabled for event and t_running is
- * the time running for event since last normalization. The
- * enabled and running times are accumulated since the perf event
- * open. To achieve scaling factor between two invocations of an
- * eBPF program, users can can use CPU id as the key (which is
- * typical for perf array usage model) to remember the previous
- * value and do the calculation inside the eBPF program.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size)
- * Description
- * For en eBPF program attached to a perf event, retrieve the
- * value of the event counter associated to *ctx* and store it in
- * the structure pointed by *buf* and of size *buf_size*. Enabled
- * and running times are also stored in the structure (see
- * description of helper **bpf_perf_event_read_value**\ () for
- * more details).
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *optval, int optlen)
- * Description
- * Emulate a call to **getsockopt()** on the socket associated to
- * *bpf_socket*, which must be a full socket. The *level* at
- * which the option resides and the name *optname* of the option
- * must be specified, see **getsockopt(2)** for more information.
- * The retrieved value is stored in the structure pointed by
- * *opval* and of length *optlen*.
- *
- * This helper actually implements a subset of **getsockopt()**.
- * It supports the following *level*\ s:
- *
- * * **IPPROTO_TCP**, which supports *optname*
- * **TCP_CONGESTION**.
- * * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
- * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_override_return(struct pt_reg *regs, u64 rc)
- * Description
- * Used for error injection, this helper uses kprobes to override
- * the return value of the probed function, and to set it to *rc*.
- * The first argument is the context *regs* on which the kprobe
- * works.
- *
- * This helper works by setting setting the PC (program counter)
- * to an override function which is run in place of the original
- * probed function. This means the probed function is not run at
- * all. The replacement function just returns with the required
- * value.
- *
- * This helper has security implications, and thus is subject to
- * restrictions. It is only available if the kernel was compiled
- * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration
- * option, and in this case it only works on functions tagged with
- * **ALLOW_ERROR_INJECTION** in the kernel code.
- *
- * Also, the helper is only available for the architectures having
- * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing,
- * x86 architecture is the only one to support this feature.
- * Return
- * 0
- *
- * int bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval)
- * Description
- * Attempt to set the value of the **bpf_sock_ops_cb_flags** field
- * for the full TCP socket associated to *bpf_sock_ops* to
- * *argval*.
- *
- * The primary use of this field is to determine if there should
- * be calls to eBPF programs of type
- * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP
- * code. A program of the same type can change its value, per
- * connection and as necessary, when the connection is
- * established. This field is directly accessible for reading, but
- * this helper must be used for updates in order to return an
- * error if an eBPF program tries to set a callback that is not
- * supported in the current kernel.
- *
- * The supported callback values that *argval* can combine are:
- *
- * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out)
- * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission)
- * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change)
- *
- * Here are some examples of where one could call such eBPF
- * program:
- *
- * * When RTO fires.
- * * When a packet is retransmitted.
- * * When the connection terminates.
- * * When a packet is sent.
- * * When a packet is received.
- * Return
- * Code **-EINVAL** if the socket is not a full TCP socket;
- * otherwise, a positive number containing the bits that could not
- * be set is returned (which comes down to 0 if all bits were set
- * as required).
- *
- * int bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags)
- * Description
- * This helper is used in programs implementing policies at the
- * socket level. If the message *msg* is allowed to pass (i.e. if
- * the verdict eBPF program returns **SK_PASS**), redirect it to
- * the socket referenced by *map* (of type
- * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
- * egress interfaces can be used for redirection. The
- * **BPF_F_INGRESS** value in *flags* is used to make the
- * distinction (ingress path is selected if the flag is present,
- * egress path otherwise). This is the only flag supported for now.
- * Return
- * **SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes)
- * Description
- * For socket policies, apply the verdict of the eBPF program to
- * the next *bytes* (number of bytes) of message *msg*.
- *
- * For example, this helper can be used in the following cases:
- *
- * * A single **sendmsg**\ () or **sendfile**\ () system call
- * contains multiple logical messages that the eBPF program is
- * supposed to read and for which it should apply a verdict.
- * * An eBPF program only cares to read the first *bytes* of a
- * *msg*. If the message has a large payload, then setting up
- * and calling the eBPF program repeatedly for all bytes, even
- * though the verdict is already known, would create unnecessary
- * overhead.
- *
- * When called from within an eBPF program, the helper sets a
- * counter internal to the BPF infrastructure, that is used to
- * apply the last verdict to the next *bytes*. If *bytes* is
- * smaller than the current data being processed from a
- * **sendmsg**\ () or **sendfile**\ () system call, the first
- * *bytes* will be sent and the eBPF program will be re-run with
- * the pointer for start of data pointing to byte number *bytes*
- * **+ 1**. If *bytes* is larger than the current data being
- * processed, then the eBPF verdict will be applied to multiple
- * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are
- * consumed.
- *
- * Note that if a socket closes with the internal counter holding
- * a non-zero value, this is not a problem because data is not
- * being buffered for *bytes* and is sent as it is received.
- * Return
- * 0
- *
- * int bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes)
- * Description
- * For socket policies, prevent the execution of the verdict eBPF
- * program for message *msg* until *bytes* (byte number) have been
- * accumulated.
- *
- * This can be used when one needs a specific number of bytes
- * before a verdict can be assigned, even if the data spans
- * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme
- * case would be a user calling **sendmsg**\ () repeatedly with
- * 1-byte long message segments. Obviously, this is bad for
- * performance, but it is still valid. If the eBPF program needs
- * *bytes* bytes to validate a header, this helper can be used to
- * prevent the eBPF program to be called again until *bytes* have
- * been accumulated.
- * Return
- * 0
- *
- * int bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags)
- * Description
- * For socket policies, pull in non-linear data from user space
- * for *msg* and set pointers *msg*\ **->data** and *msg*\
- * **->data_end** to *start* and *end* bytes offsets into *msg*,
- * respectively.
- *
- * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
- * *msg* it can only parse data that the (**data**, **data_end**)
- * pointers have already consumed. For **sendmsg**\ () hooks this
- * is likely the first scatterlist element. But for calls relying
- * on the **sendpage** handler (e.g. **sendfile**\ ()) this will
- * be the range (**0**, **0**) because the data is shared with
- * user space and by default the objective is to avoid allowing
- * user space to modify data while (or after) eBPF verdict is
- * being decided. This helper can be used to pull in data and to
- * set the start and end pointer to given values. Data will be
- * copied if necessary (i.e. if data was not linear and if start
- * and end pointers do not point to the same chunk).
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len)
- * Description
- * Bind the socket associated to *ctx* to the address pointed by
- * *addr*, of length *addr_len*. This allows for making outgoing
- * connection from the desired IP address, which can be useful for
- * example when all processes inside a cgroup should use one
- * single IP address on a host that has multiple IP configured.
- *
- * This helper works for IPv4 and IPv6, TCP and UDP sockets. The
- * domain (*addr*\ **->sa_family**) must be **AF_INET** (or
- * **AF_INET6**). Looking for a free port to bind to can be
- * expensive, therefore binding to port is not permitted by the
- * helper: *addr*\ **->sin_port** (or **sin6_port**, respectively)
- * must be set to zero.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta)
- * Description
- * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is
- * only possible to shrink the packet as of this writing,
- * therefore *delta* must be a negative integer.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags)
- * Description
- * Retrieve the XFRM state (IP transform framework, see also
- * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*.
- *
- * The retrieved value is stored in the **struct bpf_xfrm_state**
- * pointed by *xfrm_state* and of length *size*.
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * This helper is available only if the kernel was compiled with
- * **CONFIG_XFRM** configuration option.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_get_stack(struct pt_regs *regs, void *buf, u32 size, u64 flags)
- * Description
- * Return a user or a kernel stack in bpf program provided buffer.
- * To achieve this, the helper needs *ctx*, which is a pointer
- * to the context on which the tracing program is executed.
- * To store the stacktrace, the bpf program provides *buf* with
- * a nonnegative *size*.
- *
- * The last argument, *flags*, holds the number of stack frames to
- * skip (from 0 to 255), masked with
- * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
- * the following flags:
- *
- * **BPF_F_USER_STACK**
- * Collect a user space stack instead of a kernel stack.
- * **BPF_F_USER_BUILD_ID**
- * Collect buildid+offset instead of ips for user stack,
- * only valid if **BPF_F_USER_STACK** is also specified.
- *
- * **bpf_get_stack**\ () can collect up to
- * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject
- * to sufficient large buffer size. Note that
- * this limit can be controlled with the **sysctl** program, and
- * that it should be manually increased in order to profile long
- * user stacks (such as stacks for Java programs). To do so, use:
- *
- * ::
- *
- * # sysctl kernel.perf_event_max_stack=<new value>
- * Return
- * A non-negative value equal to or less than *size* on success,
- * or a negative error in case of failure.
- *
- * int bpf_skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header)
- * Description
- * This helper is similar to **bpf_skb_load_bytes**\ () in that
- * it provides an easy way to load *len* bytes from *offset*
- * from the packet associated to *skb*, into the buffer pointed
- * by *to*. The difference to **bpf_skb_load_bytes**\ () is that
- * a fifth argument *start_header* exists in order to select a
- * base offset to start from. *start_header* can be one of:
- *
- * **BPF_HDR_START_MAC**
- * Base offset to load data from is *skb*'s mac header.
- * **BPF_HDR_START_NET**
- * Base offset to load data from is *skb*'s network header.
- *
- * In general, "direct packet access" is the preferred method to
- * access packet data, however, this helper is in particular useful
- * in socket filters where *skb*\ **->data** does not always point
- * to the start of the mac header and where "direct packet access"
- * is not available.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags)
- * Description
- * Do FIB lookup in kernel tables using parameters in *params*.
- * If lookup is successful and result shows packet is to be
- * forwarded, the neighbor tables are searched for the nexthop.
- * If successful (ie., FIB lookup shows forwarding and nexthop
- * is resolved), the nexthop address is returned in ipv4_dst
- * or ipv6_dst based on family, smac is set to mac address of
- * egress device, dmac is set to nexthop mac address, rt_metric
- * is set to metric from route (IPv4/IPv6 only), and ifindex
- * is set to the device index of the nexthop from the FIB lookup.
- *
- * *plen* argument is the size of the passed in struct.
- * *flags* argument can be a combination of one or more of the
- * following values:
- *
- * **BPF_FIB_LOOKUP_DIRECT**
- * Do a direct table lookup vs full lookup using FIB
- * rules.
- * **BPF_FIB_LOOKUP_OUTPUT**
- * Perform lookup from an egress perspective (default is
- * ingress).
- *
- * *ctx* is either **struct xdp_md** for XDP programs or
- * **struct sk_buff** tc cls_act programs.
- * Return
- * * < 0 if any input argument is invalid
- * * 0 on success (packet is forwarded, nexthop neighbor exists)
- * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
- * packet is not forwarded or needs assist from full stack
- *
- * int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags)
- * Description
- * Add an entry to, or update a sockhash *map* referencing sockets.
- * The *skops* is used as a new value for the entry associated to
- * *key*. *flags* is one of:
- *
- * **BPF_NOEXIST**
- * The entry for *key* must not exist in the map.
- * **BPF_EXIST**
- * The entry for *key* must already exist in the map.
- * **BPF_ANY**
- * No condition on the existence of the entry for *key*.
- *
- * If the *map* has eBPF programs (parser and verdict), those will
- * be inherited by the socket being added. If the socket is
- * already attached to eBPF programs, this results in an error.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags)
- * Description
- * This helper is used in programs implementing policies at the
- * socket level. If the message *msg* is allowed to pass (i.e. if
- * the verdict eBPF program returns **SK_PASS**), redirect it to
- * the socket referenced by *map* (of type
- * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
- * egress interfaces can be used for redirection. The
- * **BPF_F_INGRESS** value in *flags* is used to make the
- * distinction (ingress path is selected if the flag is present,
- * egress path otherwise). This is the only flag supported for now.
- * Return
- * **SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags)
- * Description
- * This helper is used in programs implementing policies at the
- * skb socket level. If the sk_buff *skb* is allowed to pass (i.e.
- * if the verdeict eBPF program returns **SK_PASS**), redirect it
- * to the socket referenced by *map* (of type
- * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
- * egress interfaces can be used for redirection. The
- * **BPF_F_INGRESS** value in *flags* is used to make the
- * distinction (ingress path is selected if the flag is present,
- * egress otherwise). This is the only flag supported for now.
- * Return
- * **SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
- * Description
- * Encapsulate the packet associated to *skb* within a Layer 3
- * protocol header. This header is provided in the buffer at
- * address *hdr*, with *len* its size in bytes. *type* indicates
- * the protocol of the header and can be one of:
- *
- * **BPF_LWT_ENCAP_SEG6**
- * IPv6 encapsulation with Segment Routing Header
- * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH,
- * the IPv6 header is computed by the kernel.
- * **BPF_LWT_ENCAP_SEG6_INLINE**
- * Only works if *skb* contains an IPv6 packet. Insert a
- * Segment Routing Header (**struct ipv6_sr_hdr**) inside
- * the IPv6 header.
- * **BPF_LWT_ENCAP_IP**
- * IP encapsulation (GRE/GUE/IPIP/etc). The outer header
- * must be IPv4 or IPv6, followed by zero or more
- * additional headers, up to LWT_BPF_MAX_HEADROOM total
- * bytes in all prepended headers. Please note that
- * if skb_is_gso(skb) is true, no more than two headers
- * can be prepended, and the inner header, if present,
- * should be either GRE or UDP/GUE.
- *
- * BPF_LWT_ENCAP_SEG6*** types can be called by bpf programs of
- * type BPF_PROG_TYPE_LWT_IN; BPF_LWT_ENCAP_IP type can be called
- * by bpf programs of types BPF_PROG_TYPE_LWT_IN and
- * BPF_PROG_TYPE_LWT_XMIT.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len)
- * Description
- * Store *len* bytes from address *from* into the packet
- * associated to *skb*, at *offset*. Only the flags, tag and TLVs
- * inside the outermost IPv6 Segment Routing Header can be
- * modified through this helper.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta)
- * Description
- * Adjust the size allocated to TLVs in the outermost IPv6
- * Segment Routing Header contained in the packet associated to
- * *skb*, at position *offset* by *delta* bytes. Only offsets
- * after the segments are accepted. *delta* can be as well
- * positive (growing) as negative (shrinking).
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len)
- * Description
- * Apply an IPv6 Segment Routing action of type *action* to the
- * packet associated to *skb*. Each action takes a parameter
- * contained at address *param*, and of length *param_len* bytes.
- * *action* can be one of:
- *
- * **SEG6_LOCAL_ACTION_END_X**
- * End.X action: Endpoint with Layer-3 cross-connect.
- * Type of *param*: **struct in6_addr**.
- * **SEG6_LOCAL_ACTION_END_T**
- * End.T action: Endpoint with specific IPv6 table lookup.
- * Type of *param*: **int**.
- * **SEG6_LOCAL_ACTION_END_B6**
- * End.B6 action: Endpoint bound to an SRv6 policy.
- * Type of param: **struct ipv6_sr_hdr**.
- * **SEG6_LOCAL_ACTION_END_B6_ENCAP**
- * End.B6.Encap action: Endpoint bound to an SRv6
- * encapsulation policy.
- * Type of param: **struct ipv6_sr_hdr**.
- *
- * A call to this helper is susceptible to change the underlaying
- * packet buffer. Therefore, at load time, all checks on pointers
- * previously done by the verifier are invalidated and must be
- * performed again, if the helper is used in combination with
- * direct packet access.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_rc_repeat(void *ctx)
- * Description
- * This helper is used in programs implementing IR decoding, to
- * report a successfully decoded repeat key message. This delays
- * the generation of a key up event for previously generated
- * key down event.
- *
- * Some IR protocols like NEC have a special IR message for
- * repeating last button, for when a button is held down.
- *
- * The *ctx* should point to the lirc sample as passed into
- * the program.
- *
- * This helper is only available is the kernel was compiled with
- * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
- * "**y**".
- * Return
- * 0
- *
- * int bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle)
- * Description
- * This helper is used in programs implementing IR decoding, to
- * report a successfully decoded key press with *scancode*,
- * *toggle* value in the given *protocol*. The scancode will be
- * translated to a keycode using the rc keymap, and reported as
- * an input key down event. After a period a key up event is
- * generated. This period can be extended by calling either
- * **bpf_rc_keydown**\ () again with the same values, or calling
- * **bpf_rc_repeat**\ ().
- *
- * Some protocols include a toggle bit, in case the button was
- * released and pressed again between consecutive scancodes.
- *
- * The *ctx* should point to the lirc sample as passed into
- * the program.
- *
- * The *protocol* is the decoded protocol number (see
- * **enum rc_proto** for some predefined values).
- *
- * This helper is only available is the kernel was compiled with
- * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
- * "**y**".
- * Return
- * 0
- *
- * u64 bpf_skb_cgroup_id(struct sk_buff *skb)
- * Description
- * Return the cgroup v2 id of the socket associated with the *skb*.
- * This is roughly similar to the **bpf_get_cgroup_classid**\ ()
- * helper for cgroup v1 by providing a tag resp. identifier that
- * can be matched on or used for map lookups e.g. to implement
- * policy. The cgroup v2 id of a given path in the hierarchy is
- * exposed in user space through the f_handle API in order to get
- * to the same 64-bit id.
- *
- * This helper can be used on TC egress path, but not on ingress,
- * and is available only if the kernel was compiled with the
- * **CONFIG_SOCK_CGROUP_DATA** configuration option.
- * Return
- * The id is returned or 0 in case the id could not be retrieved.
- *
- * u64 bpf_get_current_cgroup_id(void)
- * Return
- * A 64-bit integer containing the current cgroup id based
- * on the cgroup within which the current task is running.
- *
- * void *bpf_get_local_storage(void *map, u64 flags)
- * Description
- * Get the pointer to the local storage area.
- * The type and the size of the local storage is defined
- * by the *map* argument.
- * The *flags* meaning is specific for each map type,
- * and has to be 0 for cgroup local storage.
- *
- * Depending on the BPF program type, a local storage area
- * can be shared between multiple instances of the BPF program,
- * running simultaneously.
- *
- * A user should care about the synchronization by himself.
- * For example, by using the **BPF_STX_XADD** instruction to alter
- * the shared data.
- * Return
- * A pointer to the local storage area.
- *
- * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags)
- * Description
- * Select a **SO_REUSEPORT** socket from a
- * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
- * It checks the selected socket is matching the incoming
- * request in the socket buffer.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level)
- * Description
- * Return id of cgroup v2 that is ancestor of cgroup associated
- * with the *skb* at the *ancestor_level*. The root cgroup is at
- * *ancestor_level* zero and each step down the hierarchy
- * increments the level. If *ancestor_level* == level of cgroup
- * associated with *skb*, then return value will be same as that
- * of **bpf_skb_cgroup_id**\ ().
- *
- * The helper is useful to implement policies based on cgroups
- * that are upper in hierarchy than immediate cgroup associated
- * with *skb*.
- *
- * The format of returned id and helper limitations are same as in
- * **bpf_skb_cgroup_id**\ ().
- * Return
- * The id is returned or 0 in case the id could not be retrieved.
- *
- * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
- * Description
- * Look for TCP socket matching *tuple*, optionally in a child
- * network namespace *netns*. The return value must be checked,
- * and if non-**NULL**, released via **bpf_sk_release**\ ().
- *
- * The *ctx* should point to the context of the program, such as
- * the skb or socket (depending on the hook in use). This is used
- * to determine the base network namespace for the lookup.
- *
- * *tuple_size* must be one of:
- *
- * **sizeof**\ (*tuple*\ **->ipv4**)
- * Look for an IPv4 socket.
- * **sizeof**\ (*tuple*\ **->ipv6**)
- * Look for an IPv6 socket.
- *
- * If the *netns* is a negative signed 32-bit integer, then the
- * socket lookup table in the netns associated with the *ctx* will
- * will be used. For the TC hooks, this is the netns of the device
- * in the skb. For socket hooks, this is the netns of the socket.
- * If *netns* is any other signed 32-bit value greater than or
- * equal to zero then it specifies the ID of the netns relative to
- * the netns associated with the *ctx*. *netns* values beyond the
- * range of 32-bit integers are reserved for future use.
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * This helper is available only if the kernel was compiled with
- * **CONFIG_NET** configuration option.
- * Return
- * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
- * For sockets with reuseport option, the **struct bpf_sock**
- * result is from **reuse->socks**\ [] using the hash of the tuple.
- *
- * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
- * Description
- * Look for UDP socket matching *tuple*, optionally in a child
- * network namespace *netns*. The return value must be checked,
- * and if non-**NULL**, released via **bpf_sk_release**\ ().
- *
- * The *ctx* should point to the context of the program, such as
- * the skb or socket (depending on the hook in use). This is used
- * to determine the base network namespace for the lookup.
- *
- * *tuple_size* must be one of:
- *
- * **sizeof**\ (*tuple*\ **->ipv4**)
- * Look for an IPv4 socket.
- * **sizeof**\ (*tuple*\ **->ipv6**)
- * Look for an IPv6 socket.
- *
- * If the *netns* is a negative signed 32-bit integer, then the
- * socket lookup table in the netns associated with the *ctx* will
- * will be used. For the TC hooks, this is the netns of the device
- * in the skb. For socket hooks, this is the netns of the socket.
- * If *netns* is any other signed 32-bit value greater than or
- * equal to zero then it specifies the ID of the netns relative to
- * the netns associated with the *ctx*. *netns* values beyond the
- * range of 32-bit integers are reserved for future use.
- *
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
- *
- * This helper is available only if the kernel was compiled with
- * **CONFIG_NET** configuration option.
- * Return
- * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
- * For sockets with reuseport option, the **struct bpf_sock**
- * result is from **reuse->socks**\ [] using the hash of the tuple.
- *
- * int bpf_sk_release(struct bpf_sock *sock)
- * Description
- * Release the reference held by *sock*. *sock* must be a
- * non-**NULL** pointer that was returned from
- * **bpf_sk_lookup_xxx**\ ().
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags)
- * Description
- * Push an element *value* in *map*. *flags* is one of:
- *
- * **BPF_EXIST**
- * If the queue/stack is full, the oldest element is
- * removed to make room for this.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_map_pop_elem(struct bpf_map *map, void *value)
- * Description
- * Pop an element from *map*.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_map_peek_elem(struct bpf_map *map, void *value)
- * Description
- * Get an element from *map* without removing it.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_msg_push_data(struct sk_buff *skb, u32 start, u32 len, u64 flags)
- * Description
- * For socket policies, insert *len* bytes into *msg* at offset
- * *start*.
- *
- * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
- * *msg* it may want to insert metadata or options into the *msg*.
- * This can later be read and used by any of the lower layer BPF
- * hooks.
- *
- * This helper may fail if under memory pressure (a malloc
- * fails) in these cases BPF programs will get an appropriate
- * error and BPF programs will need to handle them.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags)
- * Description
- * Will remove *pop* bytes from a *msg* starting at byte *start*.
- * This may result in **ENOMEM** errors under certain situations if
- * an allocation and copy are required due to a full ring buffer.
- * However, the helper will try to avoid doing the allocation
- * if possible. Other errors can occur if input parameters are
- * invalid either due to *start* byte not being valid part of *msg*
- * payload and/or *pop* value being to large.
- * Return
- * 0 on success, or a negative error in case of failure.
- *
- * int bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y)
- * Description
- * This helper is used in programs implementing IR decoding, to
- * report a successfully decoded pointer movement.
- *
- * The *ctx* should point to the lirc sample as passed into
- * the program.
- *
- * This helper is only available is the kernel was compiled with
- * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
- * "**y**".
- * Return
- * 0
- *
- * int bpf_spin_lock(struct bpf_spin_lock *lock)
- * Description
- * Acquire a spinlock represented by the pointer *lock*, which is
- * stored as part of a value of a map. Taking the lock allows to
- * safely update the rest of the fields in that value. The
- * spinlock can (and must) later be released with a call to
- * **bpf_spin_unlock**\ (\ *lock*\ ).
- *
- * Spinlocks in BPF programs come with a number of restrictions
- * and constraints:
- *
- * * **bpf_spin_lock** objects are only allowed inside maps of
- * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this
- * list could be extended in the future).
- * * BTF description of the map is mandatory.
- * * The BPF program can take ONE lock at a time, since taking two
- * or more could cause dead locks.
- * * Only one **struct bpf_spin_lock** is allowed per map element.
- * * When the lock is taken, calls (either BPF to BPF or helpers)
- * are not allowed.
- * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not
- * allowed inside a spinlock-ed region.
- * * The BPF program MUST call **bpf_spin_unlock**\ () to release
- * the lock, on all execution paths, before it returns.
- * * The BPF program can access **struct bpf_spin_lock** only via
- * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ ()
- * helpers. Loading or storing data into the **struct
- * bpf_spin_lock** *lock*\ **;** field of a map is not allowed.
- * * To use the **bpf_spin_lock**\ () helper, the BTF description
- * of the map value must be a struct and have **struct
- * bpf_spin_lock** *anyname*\ **;** field at the top level.
- * Nested lock inside another struct is not allowed.
- * * The **struct bpf_spin_lock** *lock* field in a map value must
- * be aligned on a multiple of 4 bytes in that value.
- * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy
- * the **bpf_spin_lock** field to user space.
- * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from
- * a BPF program, do not update the **bpf_spin_lock** field.
- * * **bpf_spin_lock** cannot be on the stack or inside a
- * networking packet (it can only be inside of a map values).
- * * **bpf_spin_lock** is available to root only.
- * * Tracing programs and socket filter programs cannot use
- * **bpf_spin_lock**\ () due to insufficient preemption checks
- * (but this may change in the future).
- * * **bpf_spin_lock** is not allowed in inner maps of map-in-map.
- * Return
- * 0
- *
- * int bpf_spin_unlock(struct bpf_spin_lock *lock)
- * Description
- * Release the *lock* previously locked by a call to
- * **bpf_spin_lock**\ (\ *lock*\ ).
- * Return
- * 0
- *
- * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk)
- * Description
- * This helper gets a **struct bpf_sock** pointer such
- * that all the fields in this **bpf_sock** can be accessed.
- * Return
- * A **struct bpf_sock** pointer on success, or **NULL** in
- * case of failure.
- *
- * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk)
- * Description
- * This helper gets a **struct bpf_tcp_sock** pointer from a
- * **struct bpf_sock** pointer.
- * Return
- * A **struct bpf_tcp_sock** pointer on success, or **NULL** in
- * case of failure.
- *
- * int bpf_skb_ecn_set_ce(struct sk_buf *skb)
- * Description
- * Set ECN (Explicit Congestion Notification) field of IP header
- * to **CE** (Congestion Encountered) if current value is **ECT**
- * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6
- * and IPv4.
- * Return
- * 1 if the **CE** flag is set (either by the current helper call
- * or because it was already present), 0 if it is not set.
- *
- * struct bpf_sock *bpf_get_listener_sock(struct bpf_sock *sk)
- * Description
- * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state.
- * **bpf_sk_release**\ () is unnecessary and not allowed.
- * Return
- * A **struct bpf_sock** pointer on success, or **NULL** in
- * case of failure.
- *
- * struct bpf_sock *bpf_skc_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
- * Description
- * Look for TCP socket matching *tuple*, optionally in a child
- * network namespace *netns*. The return value must be checked,
- * and if non-**NULL**, released via **bpf_sk_release**\ ().
- *
- * This function is identical to bpf_sk_lookup_tcp, except that it
- * also returns timewait or request sockets. Use bpf_sk_fullsock
- * or bpf_tcp_socket to access the full structure.
- *
- * This helper is available only if the kernel was compiled with
- * **CONFIG_NET** configuration option.
- * Return
- * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
- * For sockets with reuseport option, the **struct bpf_sock**
- * result is from **reuse->socks**\ [] using the hash of the tuple.
- *
- * int bpf_tcp_check_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
- * Description
- * Check whether iph and th contain a valid SYN cookie ACK for
- * the listening socket in sk.
- *
- * iph points to the start of the IPv4 or IPv6 header, while
- * iph_len contains sizeof(struct iphdr) or sizeof(struct ip6hdr).
- *
- * th points to the start of the TCP header, while th_len contains
- * sizeof(struct tcphdr).
- *
- * Return
- * 0 if iph and th are a valid SYN cookie ACK, or a negative error
- * otherwise.
- */
-#define __BPF_FUNC_MAPPER(FN) \
- FN(unspec), \
- FN(map_lookup_elem), \
- FN(map_update_elem), \
- FN(map_delete_elem), \
- FN(probe_read), \
- FN(ktime_get_ns), \
- FN(trace_printk), \
- FN(get_prandom_u32), \
- FN(get_smp_processor_id), \
- FN(skb_store_bytes), \
- FN(l3_csum_replace), \
- FN(l4_csum_replace), \
- FN(tail_call), \
- FN(clone_redirect), \
- FN(get_current_pid_tgid), \
- FN(get_current_uid_gid), \
- FN(get_current_comm), \
- FN(get_cgroup_classid), \
- FN(skb_vlan_push), \
- FN(skb_vlan_pop), \
- FN(skb_get_tunnel_key), \
- FN(skb_set_tunnel_key), \
- FN(perf_event_read), \
- FN(redirect), \
- FN(get_route_realm), \
- FN(perf_event_output), \
- FN(skb_load_bytes), \
- FN(get_stackid), \
- FN(csum_diff), \
- FN(skb_get_tunnel_opt), \
- FN(skb_set_tunnel_opt), \
- FN(skb_change_proto), \
- FN(skb_change_type), \
- FN(skb_under_cgroup), \
- FN(get_hash_recalc), \
- FN(get_current_task), \
- FN(probe_write_user), \
- FN(current_task_under_cgroup), \
- FN(skb_change_tail), \
- FN(skb_pull_data), \
- FN(csum_update), \
- FN(set_hash_invalid), \
- FN(get_numa_node_id), \
- FN(skb_change_head), \
- FN(xdp_adjust_head), \
- FN(probe_read_str), \
- FN(get_socket_cookie), \
- FN(get_socket_uid), \
- FN(set_hash), \
- FN(setsockopt), \
- FN(skb_adjust_room), \
- FN(redirect_map), \
- FN(sk_redirect_map), \
- FN(sock_map_update), \
- FN(xdp_adjust_meta), \
- FN(perf_event_read_value), \
- FN(perf_prog_read_value), \
- FN(getsockopt), \
- FN(override_return), \
- FN(sock_ops_cb_flags_set), \
- FN(msg_redirect_map), \
- FN(msg_apply_bytes), \
- FN(msg_cork_bytes), \
- FN(msg_pull_data), \
- FN(bind), \
- FN(xdp_adjust_tail), \
- FN(skb_get_xfrm_state), \
- FN(get_stack), \
- FN(skb_load_bytes_relative), \
- FN(fib_lookup), \
- FN(sock_hash_update), \
- FN(msg_redirect_hash), \
- FN(sk_redirect_hash), \
- FN(lwt_push_encap), \
- FN(lwt_seg6_store_bytes), \
- FN(lwt_seg6_adjust_srh), \
- FN(lwt_seg6_action), \
- FN(rc_repeat), \
- FN(rc_keydown), \
- FN(skb_cgroup_id), \
- FN(get_current_cgroup_id), \
- FN(get_local_storage), \
- FN(sk_select_reuseport), \
- FN(skb_ancestor_cgroup_id), \
- FN(sk_lookup_tcp), \
- FN(sk_lookup_udp), \
- FN(sk_release), \
- FN(map_push_elem), \
- FN(map_pop_elem), \
- FN(map_peek_elem), \
- FN(msg_push_data), \
- FN(msg_pop_data), \
- FN(rc_pointer_rel), \
- FN(spin_lock), \
- FN(spin_unlock), \
- FN(sk_fullsock), \
- FN(tcp_sock), \
- FN(skb_ecn_set_ce), \
- FN(get_listener_sock), \
- FN(skc_lookup_tcp), \
- FN(tcp_check_syncookie),
-
-/* integer value in 'imm' field of BPF_CALL instruction selects which helper
- * function eBPF program intends to call
- */
-#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x
-enum bpf_func_id {
- __BPF_FUNC_MAPPER(__BPF_ENUM_FN)
- __BPF_FUNC_MAX_ID,
-};
-#undef __BPF_ENUM_FN
-
-/* All flags used by eBPF helper functions, placed here. */
-
-/* BPF_FUNC_skb_store_bytes flags. */
-#define BPF_F_RECOMPUTE_CSUM (1ULL << 0)
-#define BPF_F_INVALIDATE_HASH (1ULL << 1)
-
-/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags.
- * First 4 bits are for passing the header field size.
- */
-#define BPF_F_HDR_FIELD_MASK 0xfULL
-
-/* BPF_FUNC_l4_csum_replace flags. */
-#define BPF_F_PSEUDO_HDR (1ULL << 4)
-#define BPF_F_MARK_MANGLED_0 (1ULL << 5)
-#define BPF_F_MARK_ENFORCE (1ULL << 6)
-
-/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
-#define BPF_F_INGRESS (1ULL << 0)
-
-/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
-#define BPF_F_TUNINFO_IPV6 (1ULL << 0)
-
-/* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */
-#define BPF_F_SKIP_FIELD_MASK 0xffULL
-#define BPF_F_USER_STACK (1ULL << 8)
-/* flags used by BPF_FUNC_get_stackid only. */
-#define BPF_F_FAST_STACK_CMP (1ULL << 9)
-#define BPF_F_REUSE_STACKID (1ULL << 10)
-/* flags used by BPF_FUNC_get_stack only. */
-#define BPF_F_USER_BUILD_ID (1ULL << 11)
-
-/* BPF_FUNC_skb_set_tunnel_key flags. */
-#define BPF_F_ZERO_CSUM_TX (1ULL << 1)
-#define BPF_F_DONT_FRAGMENT (1ULL << 2)
-#define BPF_F_SEQ_NUMBER (1ULL << 3)
-
-/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
- * BPF_FUNC_perf_event_read_value flags.
- */
-#define BPF_F_INDEX_MASK 0xffffffffULL
-#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
-/* BPF_FUNC_perf_event_output for sk_buff input context. */
-#define BPF_F_CTXLEN_MASK (0xfffffULL << 32)
-
-/* Current network namespace */
-#define BPF_F_CURRENT_NETNS (-1L)
-
-/* BPF_FUNC_skb_adjust_room flags. */
-#define BPF_F_ADJ_ROOM_FIXED_GSO (1ULL << 0)
-
-#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 (1ULL << 1)
-#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 (1ULL << 2)
-#define BPF_F_ADJ_ROOM_ENCAP_L4_GRE (1ULL << 3)
-#define BPF_F_ADJ_ROOM_ENCAP_L4_UDP (1ULL << 4)
-
-/* Mode for BPF_FUNC_skb_adjust_room helper. */
-enum bpf_adj_room_mode {
- BPF_ADJ_ROOM_NET,
- BPF_ADJ_ROOM_MAC,
-};
-
-/* Mode for BPF_FUNC_skb_load_bytes_relative helper. */
-enum bpf_hdr_start_off {
- BPF_HDR_START_MAC,
- BPF_HDR_START_NET,
-};
-
-/* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
-enum bpf_lwt_encap_mode {
- BPF_LWT_ENCAP_SEG6,
- BPF_LWT_ENCAP_SEG6_INLINE,
- BPF_LWT_ENCAP_IP,
-};
-
-#define __bpf_md_ptr(type, name) \
-union { \
- type name; \
- __u64 :64; \
-} __attribute__((aligned(8)))
-
-/* user accessible mirror of in-kernel sk_buff.
- * new fields can only be added to the end of this structure
- */
-struct __sk_buff {
- __u32 len;
- __u32 pkt_type;
- __u32 mark;
- __u32 queue_mapping;
- __u32 protocol;
- __u32 vlan_present;
- __u32 vlan_tci;
- __u32 vlan_proto;
- __u32 priority;
- __u32 ingress_ifindex;
- __u32 ifindex;
- __u32 tc_index;
- __u32 cb[5];
- __u32 hash;
- __u32 tc_classid;
- __u32 data;
- __u32 data_end;
- __u32 napi_id;
-
- /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
- __u32 family;
- __u32 remote_ip4; /* Stored in network byte order */
- __u32 local_ip4; /* Stored in network byte order */
- __u32 remote_ip6[4]; /* Stored in network byte order */
- __u32 local_ip6[4]; /* Stored in network byte order */
- __u32 remote_port; /* Stored in network byte order */
- __u32 local_port; /* stored in host byte order */
- /* ... here. */
-
- __u32 data_meta;
- __bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
- __u64 tstamp;
- __u32 wire_len;
- __u32 gso_segs;
- __bpf_md_ptr(struct bpf_sock *, sk);
-};
-
-struct bpf_tunnel_key {
- __u32 tunnel_id;
- union {
- __u32 remote_ipv4;
- __u32 remote_ipv6[4];
- };
- __u8 tunnel_tos;
- __u8 tunnel_ttl;
- __u16 tunnel_ext; /* Padding, future use. */
- __u32 tunnel_label;
-};
-
-/* user accessible mirror of in-kernel xfrm_state.
- * new fields can only be added to the end of this structure
- */
-struct bpf_xfrm_state {
- __u32 reqid;
- __u32 spi; /* Stored in network byte order */
- __u16 family;
- __u16 ext; /* Padding, future use. */
- union {
- __u32 remote_ipv4; /* Stored in network byte order */
- __u32 remote_ipv6[4]; /* Stored in network byte order */
- };
-};
-
-/* Generic BPF return codes which all BPF program types may support.
- * The values are binary compatible with their TC_ACT_* counter-part to
- * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT
- * programs.
- *
- * XDP is handled seprately, see XDP_*.
- */
-enum bpf_ret_code {
- BPF_OK = 0,
- /* 1 reserved */
- BPF_DROP = 2,
- /* 3-6 reserved */
- BPF_REDIRECT = 7,
- /* >127 are reserved for prog type specific return codes.
- *
- * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and
- * BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been
- * changed and should be routed based on its new L3 header.
- * (This is an L3 redirect, as opposed to L2 redirect
- * represented by BPF_REDIRECT above).
- */
- BPF_LWT_REROUTE = 128,
-};
-
-struct bpf_sock {
- __u32 bound_dev_if;
- __u32 family;
- __u32 type;
- __u32 protocol;
- __u32 mark;
- __u32 priority;
- /* IP address also allows 1 and 2 bytes access */
- __u32 src_ip4;
- __u32 src_ip6[4];
- __u32 src_port; /* host byte order */
- __u32 dst_port; /* network byte order */
- __u32 dst_ip4;
- __u32 dst_ip6[4];
- __u32 state;
-};
-
-struct bpf_tcp_sock {
- __u32 snd_cwnd; /* Sending congestion window */
- __u32 srtt_us; /* smoothed round trip time << 3 in usecs */
- __u32 rtt_min;
- __u32 snd_ssthresh; /* Slow start size threshold */
- __u32 rcv_nxt; /* What we want to receive next */
- __u32 snd_nxt; /* Next sequence we send */
- __u32 snd_una; /* First byte we want an ack for */
- __u32 mss_cache; /* Cached effective mss, not including SACKS */
- __u32 ecn_flags; /* ECN status bits. */
- __u32 rate_delivered; /* saved rate sample: packets delivered */
- __u32 rate_interval_us; /* saved rate sample: time elapsed */
- __u32 packets_out; /* Packets which are "in flight" */
- __u32 retrans_out; /* Retransmitted packets out */
- __u32 total_retrans; /* Total retransmits for entire connection */
- __u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn
- * total number of segments in.
- */
- __u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn
- * total number of data segments in.
- */
- __u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut
- * The total number of segments sent.
- */
- __u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut
- * total number of data segments sent.
- */
- __u32 lost_out; /* Lost packets */
- __u32 sacked_out; /* SACK'd packets */
- __u64 bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived
- * sum(delta(rcv_nxt)), or how many bytes
- * were acked.
- */
- __u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked
- * sum(delta(snd_una)), or how many bytes
- * were acked.
- */
-};
-
-struct bpf_sock_tuple {
- union {
- struct {
- __be32 saddr;
- __be32 daddr;
- __be16 sport;
- __be16 dport;
- } ipv4;
- struct {
- __be32 saddr[4];
- __be32 daddr[4];
- __be16 sport;
- __be16 dport;
- } ipv6;
- };
-};
-
-#define XDP_PACKET_HEADROOM 256
-
-/* User return codes for XDP prog type.
- * A valid XDP program must return one of these defined values. All other
- * return codes are reserved for future use. Unknown return codes will
- * result in packet drops and a warning via bpf_warn_invalid_xdp_action().
- */
-enum xdp_action {
- XDP_ABORTED = 0,
- XDP_DROP,
- XDP_PASS,
- XDP_TX,
- XDP_REDIRECT,
-};
-
-/* user accessible metadata for XDP packet hook
- * new fields must be added to the end of this structure
- */
-struct xdp_md {
- __u32 data;
- __u32 data_end;
- __u32 data_meta;
- /* Below access go through struct xdp_rxq_info */
- __u32 ingress_ifindex; /* rxq->dev->ifindex */
- __u32 rx_queue_index; /* rxq->queue_index */
-};
-
-enum sk_action {
- SK_DROP = 0,
- SK_PASS,
-};
-
-/* user accessible metadata for SK_MSG packet hook, new fields must
- * be added to the end of this structure
- */
-struct sk_msg_md {
- __bpf_md_ptr(void *, data);
- __bpf_md_ptr(void *, data_end);
-
- __u32 family;
- __u32 remote_ip4; /* Stored in network byte order */
- __u32 local_ip4; /* Stored in network byte order */
- __u32 remote_ip6[4]; /* Stored in network byte order */
- __u32 local_ip6[4]; /* Stored in network byte order */
- __u32 remote_port; /* Stored in network byte order */
- __u32 local_port; /* stored in host byte order */
- __u32 size; /* Total size of sk_msg */
-};
-
-struct sk_reuseport_md {
- /*
- * Start of directly accessible data. It begins from
- * the tcp/udp header.
- */
- __bpf_md_ptr(void *, data);
- /* End of directly accessible data */
- __bpf_md_ptr(void *, data_end);
- /*
- * Total length of packet (starting from the tcp/udp header).
- * Note that the directly accessible bytes (data_end - data)
- * could be less than this "len". Those bytes could be
- * indirectly read by a helper "bpf_skb_load_bytes()".
- */
- __u32 len;
- /*
- * Eth protocol in the mac header (network byte order). e.g.
- * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD)
- */
- __u32 eth_protocol;
- __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */
- __u32 bind_inany; /* Is sock bound to an INANY address? */
- __u32 hash; /* A hash of the packet 4 tuples */
-};
-
-#define BPF_TAG_SIZE 8
-
-struct bpf_prog_info {
- __u32 type;
- __u32 id;
- __u8 tag[BPF_TAG_SIZE];
- __u32 jited_prog_len;
- __u32 xlated_prog_len;
- __aligned_u64 jited_prog_insns;
- __aligned_u64 xlated_prog_insns;
- __u64 load_time; /* ns since boottime */
- __u32 created_by_uid;
- __u32 nr_map_ids;
- __aligned_u64 map_ids;
- char name[BPF_OBJ_NAME_LEN];
- __u32 ifindex;
- __u32 gpl_compatible:1;
- __u64 netns_dev;
- __u64 netns_ino;
- __u32 nr_jited_ksyms;
- __u32 nr_jited_func_lens;
- __aligned_u64 jited_ksyms;
- __aligned_u64 jited_func_lens;
- __u32 btf_id;
- __u32 func_info_rec_size;
- __aligned_u64 func_info;
- __u32 nr_func_info;
- __u32 nr_line_info;
- __aligned_u64 line_info;
- __aligned_u64 jited_line_info;
- __u32 nr_jited_line_info;
- __u32 line_info_rec_size;
- __u32 jited_line_info_rec_size;
- __u32 nr_prog_tags;
- __aligned_u64 prog_tags;
- __u64 run_time_ns;
- __u64 run_cnt;
-} __attribute__((aligned(8)));
-
-struct bpf_map_info {
- __u32 type;
- __u32 id;
- __u32 key_size;
- __u32 value_size;
- __u32 max_entries;
- __u32 map_flags;
- char name[BPF_OBJ_NAME_LEN];
- __u32 ifindex;
- __u32 :32;
- __u64 netns_dev;
- __u64 netns_ino;
- __u32 btf_id;
- __u32 btf_key_type_id;
- __u32 btf_value_type_id;
-} __attribute__((aligned(8)));
-
-struct bpf_btf_info {
- __aligned_u64 btf;
- __u32 btf_size;
- __u32 id;
-} __attribute__((aligned(8)));
-
-/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
- * by user and intended to be used by socket (e.g. to bind to, depends on
- * attach attach type).
- */
-struct bpf_sock_addr {
- __u32 user_family; /* Allows 4-byte read, but no write. */
- __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
- * Stored in network byte order.
- */
- __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
- * Stored in network byte order.
- */
- __u32 user_port; /* Allows 4-byte read and write.
- * Stored in network byte order
- */
- __u32 family; /* Allows 4-byte read, but no write */
- __u32 type; /* Allows 4-byte read, but no write */
- __u32 protocol; /* Allows 4-byte read, but no write */
- __u32 msg_src_ip4; /* Allows 1,2,4-byte read an 4-byte write.
- * Stored in network byte order.
- */
- __u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
- * Stored in network byte order.
- */
-};
-
-/* User bpf_sock_ops struct to access socket values and specify request ops
- * and their replies.
- * Some of this fields are in network (bigendian) byte order and may need
- * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h).
- * New fields can only be added at the end of this structure
- */
-struct bpf_sock_ops {
- __u32 op;
- union {
- __u32 args[4]; /* Optionally passed to bpf program */
- __u32 reply; /* Returned by bpf program */
- __u32 replylong[4]; /* Optionally returned by bpf prog */
- };
- __u32 family;
- __u32 remote_ip4; /* Stored in network byte order */
- __u32 local_ip4; /* Stored in network byte order */
- __u32 remote_ip6[4]; /* Stored in network byte order */
- __u32 local_ip6[4]; /* Stored in network byte order */
- __u32 remote_port; /* Stored in network byte order */
- __u32 local_port; /* stored in host byte order */
- __u32 is_fullsock; /* Some TCP fields are only valid if
- * there is a full socket. If not, the
- * fields read as zero.
- */
- __u32 snd_cwnd;
- __u32 srtt_us; /* Averaged RTT << 3 in usecs */
- __u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */
- __u32 state;
- __u32 rtt_min;
- __u32 snd_ssthresh;
- __u32 rcv_nxt;
- __u32 snd_nxt;
- __u32 snd_una;
- __u32 mss_cache;
- __u32 ecn_flags;
- __u32 rate_delivered;
- __u32 rate_interval_us;
- __u32 packets_out;
- __u32 retrans_out;
- __u32 total_retrans;
- __u32 segs_in;
- __u32 data_segs_in;
- __u32 segs_out;
- __u32 data_segs_out;
- __u32 lost_out;
- __u32 sacked_out;
- __u32 sk_txhash;
- __u64 bytes_received;
- __u64 bytes_acked;
-};
-
-/* Definitions for bpf_sock_ops_cb_flags */
-#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0)
-#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1)
-#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2)
-#define BPF_SOCK_OPS_ALL_CB_FLAGS 0x7 /* Mask of all currently
- * supported cb flags
- */
-
-/* List of known BPF sock_ops operators.
- * New entries can only be added at the end
- */
-enum {
- BPF_SOCK_OPS_VOID,
- BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or
- * -1 if default value should be used
- */
- BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized
- * window (in packets) or -1 if default
- * value should be used
- */
- BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an
- * active connection is initialized
- */
- BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an
- * active connection is
- * established
- */
- BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a
- * passive connection is
- * established
- */
- BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control
- * needs ECN
- */
- BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is
- * based on the path and may be
- * dependent on the congestion control
- * algorithm. In general it indicates
- * a congestion threshold. RTTs above
- * this indicate congestion
- */
- BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered.
- * Arg1: value of icsk_retransmits
- * Arg2: value of icsk_rto
- * Arg3: whether RTO has expired
- */
- BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted.
- * Arg1: sequence number of 1st byte
- * Arg2: # segments
- * Arg3: return value of
- * tcp_transmit_skb (0 => success)
- */
- BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state.
- * Arg1: old_state
- * Arg2: new_state
- */
- BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
- * socket transition to LISTEN state.
- */
-};
-
-/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
- * changes between the TCP and BPF versions. Ideally this should never happen.
- * If it does, we need to add code to convert them before calling
- * the BPF sock_ops function.
- */
-enum {
- BPF_TCP_ESTABLISHED = 1,
- BPF_TCP_SYN_SENT,
- BPF_TCP_SYN_RECV,
- BPF_TCP_FIN_WAIT1,
- BPF_TCP_FIN_WAIT2,
- BPF_TCP_TIME_WAIT,
- BPF_TCP_CLOSE,
- BPF_TCP_CLOSE_WAIT,
- BPF_TCP_LAST_ACK,
- BPF_TCP_LISTEN,
- BPF_TCP_CLOSING, /* Now a valid state */
- BPF_TCP_NEW_SYN_RECV,
-
- BPF_TCP_MAX_STATES /* Leave at the end! */
-};
-
-#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
-#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */
-
-struct bpf_perf_event_value {
- __u64 counter;
- __u64 enabled;
- __u64 running;
-};
-
-#define BPF_DEVCG_ACC_MKNOD (1ULL << 0)
-#define BPF_DEVCG_ACC_READ (1ULL << 1)
-#define BPF_DEVCG_ACC_WRITE (1ULL << 2)
-
-#define BPF_DEVCG_DEV_BLOCK (1ULL << 0)
-#define BPF_DEVCG_DEV_CHAR (1ULL << 1)
-
-struct bpf_cgroup_dev_ctx {
- /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */
- __u32 access_type;
- __u32 major;
- __u32 minor;
-};
-
-struct bpf_raw_tracepoint_args {
- __u64 args[0];
-};
-
-/* DIRECT: Skip the FIB rules and go to FIB table associated with device
- * OUTPUT: Do lookup from egress perspective; default is ingress
- */
-#define BPF_FIB_LOOKUP_DIRECT BIT(0)
-#define BPF_FIB_LOOKUP_OUTPUT BIT(1)
-
-enum {
- BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */
- BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */
- BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */
- BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */
- BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */
- BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */
- BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */
- BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */
- BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */
-};
-
-struct bpf_fib_lookup {
- /* input: network family for lookup (AF_INET, AF_INET6)
- * output: network family of egress nexthop
- */
- __u8 family;
-
- /* set if lookup is to consider L4 data - e.g., FIB rules */
- __u8 l4_protocol;
- __be16 sport;
- __be16 dport;
-
- /* total length of packet from network header - used for MTU check */
- __u16 tot_len;
-
- /* input: L3 device index for lookup
- * output: device index from FIB lookup
- */
- __u32 ifindex;
-
- union {
- /* inputs to lookup */
- __u8 tos; /* AF_INET */
- __be32 flowinfo; /* AF_INET6, flow_label + priority */
-
- /* output: metric of fib result (IPv4/IPv6 only) */
- __u32 rt_metric;
- };
-
- union {
- __be32 ipv4_src;
- __u32 ipv6_src[4]; /* in6_addr; network order */
- };
-
- /* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in
- * network header. output: bpf_fib_lookup sets to gateway address
- * if FIB lookup returns gateway route
- */
- union {
- __be32 ipv4_dst;
- __u32 ipv6_dst[4]; /* in6_addr; network order */
- };
-
- /* output */
- __be16 h_vlan_proto;
- __be16 h_vlan_TCI;
- __u8 smac[6]; /* ETH_ALEN */
- __u8 dmac[6]; /* ETH_ALEN */
-};
-
-enum bpf_task_fd_type {
- BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */
- BPF_FD_TYPE_TRACEPOINT, /* tp name */
- BPF_FD_TYPE_KPROBE, /* (symbol + offset) or addr */
- BPF_FD_TYPE_KRETPROBE, /* (symbol + offset) or addr */
- BPF_FD_TYPE_UPROBE, /* filename + offset */
- BPF_FD_TYPE_URETPROBE, /* filename + offset */
-};
-
-struct bpf_flow_keys {
- __u16 nhoff;
- __u16 thoff;
- __u16 addr_proto; /* ETH_P_* of valid addrs */
- __u8 is_frag;
- __u8 is_first_frag;
- __u8 is_encap;
- __u8 ip_proto;
- __be16 n_proto;
- __be16 sport;
- __be16 dport;
- union {
- struct {
- __be32 ipv4_src;
- __be32 ipv4_dst;
- };
- struct {
- __u32 ipv6_src[4]; /* in6_addr; network order */
- __u32 ipv6_dst[4]; /* in6_addr; network order */
- };
- };
-};
-
-struct bpf_func_info {
- __u32 insn_off;
- __u32 type_id;
-};
-
-#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10)
-#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff)
-
-struct bpf_line_info {
- __u32 insn_off;
- __u32 file_name_off;
- __u32 line_off;
- __u32 line_col;
-};
-
-struct bpf_spin_lock {
- __u32 val;
-};
-#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/src/xdp/include/linux/err.h b/src/xdp/include/linux/err.h
deleted file mode 100644
index a3db4cf5e..000000000
--- a/src/xdp/include/linux/err.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
-
-#ifndef __LINUX_ERR_H
-#define __LINUX_ERR_H
-
-#include <linux/types.h>
-#include <asm/errno.h>
-
-#define MAX_ERRNO 4095
-
-#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
-
-static inline void * ERR_PTR(long error_)
-{
- return (void *) error_;
-}
-
-static inline long PTR_ERR(const void *ptr)
-{
- return (long) ptr;
-}
-
-static inline bool IS_ERR(const void *ptr)
-{
- return IS_ERR_VALUE((unsigned long)ptr);
-}
-
-static inline bool IS_ERR_OR_NULL(const void *ptr)
-{
- return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
-}
-
-#endif
diff --git a/src/xdp/include/linux/if_link.h b/src/xdp/include/linux/if_link.h
deleted file mode 100644
index 5b225ff63..000000000
--- a/src/xdp/include/linux/if_link.h
+++ /dev/null
@@ -1,1025 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _UAPI_LINUX_IF_LINK_H
-#define _UAPI_LINUX_IF_LINK_H
-
-#include <linux/types.h>
-#include <linux/netlink.h>
-
-/* This struct should be in sync with struct rtnl_link_stats64 */
-struct rtnl_link_stats {
- __u32 rx_packets; /* total packets received */
- __u32 tx_packets; /* total packets transmitted */
- __u32 rx_bytes; /* total bytes received */
- __u32 tx_bytes; /* total bytes transmitted */
- __u32 rx_errors; /* bad packets received */
- __u32 tx_errors; /* packet transmit problems */
- __u32 rx_dropped; /* no space in linux buffers */
- __u32 tx_dropped; /* no space available in linux */
- __u32 multicast; /* multicast packets received */
- __u32 collisions;
-
- /* detailed rx_errors: */
- __u32 rx_length_errors;
- __u32 rx_over_errors; /* receiver ring buff overflow */
- __u32 rx_crc_errors; /* recved pkt with crc error */
- __u32 rx_frame_errors; /* recv'd frame alignment error */
- __u32 rx_fifo_errors; /* recv'r fifo overrun */
- __u32 rx_missed_errors; /* receiver missed packet */
-
- /* detailed tx_errors */
- __u32 tx_aborted_errors;
- __u32 tx_carrier_errors;
- __u32 tx_fifo_errors;
- __u32 tx_heartbeat_errors;
- __u32 tx_window_errors;
-
- /* for cslip etc */
- __u32 rx_compressed;
- __u32 tx_compressed;
-
- __u32 rx_nohandler; /* dropped, no handler found */
-};
-
-/* The main device statistics structure */
-struct rtnl_link_stats64 {
- __u64 rx_packets; /* total packets received */
- __u64 tx_packets; /* total packets transmitted */
- __u64 rx_bytes; /* total bytes received */
- __u64 tx_bytes; /* total bytes transmitted */
- __u64 rx_errors; /* bad packets received */
- __u64 tx_errors; /* packet transmit problems */
- __u64 rx_dropped; /* no space in linux buffers */
- __u64 tx_dropped; /* no space available in linux */
- __u64 multicast; /* multicast packets received */
- __u64 collisions;
-
- /* detailed rx_errors: */
- __u64 rx_length_errors;
- __u64 rx_over_errors; /* receiver ring buff overflow */
- __u64 rx_crc_errors; /* recved pkt with crc error */
- __u64 rx_frame_errors; /* recv'd frame alignment error */
- __u64 rx_fifo_errors; /* recv'r fifo overrun */
- __u64 rx_missed_errors; /* receiver missed packet */
-
- /* detailed tx_errors */
- __u64 tx_aborted_errors;
- __u64 tx_carrier_errors;
- __u64 tx_fifo_errors;
- __u64 tx_heartbeat_errors;
- __u64 tx_window_errors;
-
- /* for cslip etc */
- __u64 rx_compressed;
- __u64 tx_compressed;
-
- __u64 rx_nohandler; /* dropped, no handler found */
-};
-
-/* The struct should be in sync with struct ifmap */
-struct rtnl_link_ifmap {
- __u64 mem_start;
- __u64 mem_end;
- __u64 base_addr;
- __u16 irq;
- __u8 dma;
- __u8 port;
-};
-
-/*
- * IFLA_AF_SPEC
- * Contains nested attributes for address family specific attributes.
- * Each address family may create a attribute with the address family
- * number as type and create its own attribute structure in it.
- *
- * Example:
- * [IFLA_AF_SPEC] = {
- * [AF_INET] = {
- * [IFLA_INET_CONF] = ...,
- * },
- * [AF_INET6] = {
- * [IFLA_INET6_FLAGS] = ...,
- * [IFLA_INET6_CONF] = ...,
- * }
- * }
- */
-
-enum {
- IFLA_UNSPEC,
- IFLA_ADDRESS,
- IFLA_BROADCAST,
- IFLA_IFNAME,
- IFLA_MTU,
- IFLA_LINK,
- IFLA_QDISC,
- IFLA_STATS,
- IFLA_COST,
-#define IFLA_COST IFLA_COST
- IFLA_PRIORITY,
-#define IFLA_PRIORITY IFLA_PRIORITY
- IFLA_MASTER,
-#define IFLA_MASTER IFLA_MASTER
- IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */
-#define IFLA_WIRELESS IFLA_WIRELESS
- IFLA_PROTINFO, /* Protocol specific information for a link */
-#define IFLA_PROTINFO IFLA_PROTINFO
- IFLA_TXQLEN,
-#define IFLA_TXQLEN IFLA_TXQLEN
- IFLA_MAP,
-#define IFLA_MAP IFLA_MAP
- IFLA_WEIGHT,
-#define IFLA_WEIGHT IFLA_WEIGHT
- IFLA_OPERSTATE,
- IFLA_LINKMODE,
- IFLA_LINKINFO,
-#define IFLA_LINKINFO IFLA_LINKINFO
- IFLA_NET_NS_PID,
- IFLA_IFALIAS,
- IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */
- IFLA_VFINFO_LIST,
- IFLA_STATS64,
- IFLA_VF_PORTS,
- IFLA_PORT_SELF,
- IFLA_AF_SPEC,
- IFLA_GROUP, /* Group the device belongs to */
- IFLA_NET_NS_FD,
- IFLA_EXT_MASK, /* Extended info mask, VFs, etc */
- IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */
-#define IFLA_PROMISCUITY IFLA_PROMISCUITY
- IFLA_NUM_TX_QUEUES,
- IFLA_NUM_RX_QUEUES,
- IFLA_CARRIER,
- IFLA_PHYS_PORT_ID,
- IFLA_CARRIER_CHANGES,
- IFLA_PHYS_SWITCH_ID,
- IFLA_LINK_NETNSID,
- IFLA_PHYS_PORT_NAME,
- IFLA_PROTO_DOWN,
- IFLA_GSO_MAX_SEGS,
- IFLA_GSO_MAX_SIZE,
- IFLA_PAD,
- IFLA_XDP,
- IFLA_EVENT,
- IFLA_NEW_NETNSID,
- IFLA_IF_NETNSID,
- IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */
- IFLA_CARRIER_UP_COUNT,
- IFLA_CARRIER_DOWN_COUNT,
- IFLA_NEW_IFINDEX,
- IFLA_MIN_MTU,
- IFLA_MAX_MTU,
- __IFLA_MAX
-};
-
-
-#define IFLA_MAX (__IFLA_MAX - 1)
-
-/* backwards compatibility for userspace */
-#ifndef __KERNEL__
-#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
-#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
-#endif
-
-enum {
- IFLA_INET_UNSPEC,
- IFLA_INET_CONF,
- __IFLA_INET_MAX,
-};
-
-#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
-
-/* ifi_flags.
-
- IFF_* flags.
-
- The only change is:
- IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are
- more not changeable by user. They describe link media
- characteristics and set by device driver.
-
- Comments:
- - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid
- - If neither of these three flags are set;
- the interface is NBMA.
-
- - IFF_MULTICAST does not mean anything special:
- multicasts can be used on all not-NBMA links.
- IFF_MULTICAST means that this media uses special encapsulation
- for multicast frames. Apparently, all IFF_POINTOPOINT and
- IFF_BROADCAST devices are able to use multicasts too.
- */
-
-/* IFLA_LINK.
- For usual devices it is equal ifi_index.
- If it is a "virtual interface" (f.e. tunnel), ifi_link
- can point to real physical interface (f.e. for bandwidth calculations),
- or maybe 0, what means, that real media is unknown (usual
- for IPIP tunnels, when route to endpoint is allowed to change)
- */
-
-/* Subtype attributes for IFLA_PROTINFO */
-enum {
- IFLA_INET6_UNSPEC,
- IFLA_INET6_FLAGS, /* link flags */
- IFLA_INET6_CONF, /* sysctl parameters */
- IFLA_INET6_STATS, /* statistics */
- IFLA_INET6_MCAST, /* MC things. What of them? */
- IFLA_INET6_CACHEINFO, /* time values and max reasm size */
- IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */
- IFLA_INET6_TOKEN, /* device token */
- IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */
- __IFLA_INET6_MAX
-};
-
-#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
-
-enum in6_addr_gen_mode {
- IN6_ADDR_GEN_MODE_EUI64,
- IN6_ADDR_GEN_MODE_NONE,
- IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
- IN6_ADDR_GEN_MODE_RANDOM,
-};
-
-/* Bridge section */
-
-enum {
- IFLA_BR_UNSPEC,
- IFLA_BR_FORWARD_DELAY,
- IFLA_BR_HELLO_TIME,
- IFLA_BR_MAX_AGE,
- IFLA_BR_AGEING_TIME,
- IFLA_BR_STP_STATE,
- IFLA_BR_PRIORITY,
- IFLA_BR_VLAN_FILTERING,
- IFLA_BR_VLAN_PROTOCOL,
- IFLA_BR_GROUP_FWD_MASK,
- IFLA_BR_ROOT_ID,
- IFLA_BR_BRIDGE_ID,
- IFLA_BR_ROOT_PORT,
- IFLA_BR_ROOT_PATH_COST,
- IFLA_BR_TOPOLOGY_CHANGE,
- IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
- IFLA_BR_HELLO_TIMER,
- IFLA_BR_TCN_TIMER,
- IFLA_BR_TOPOLOGY_CHANGE_TIMER,
- IFLA_BR_GC_TIMER,
- IFLA_BR_GROUP_ADDR,
- IFLA_BR_FDB_FLUSH,
- IFLA_BR_MCAST_ROUTER,
- IFLA_BR_MCAST_SNOOPING,
- IFLA_BR_MCAST_QUERY_USE_IFADDR,
- IFLA_BR_MCAST_QUERIER,
- IFLA_BR_MCAST_HASH_ELASTICITY,
- IFLA_BR_MCAST_HASH_MAX,
- IFLA_BR_MCAST_LAST_MEMBER_CNT,
- IFLA_BR_MCAST_STARTUP_QUERY_CNT,
- IFLA_BR_MCAST_LAST_MEMBER_INTVL,
- IFLA_BR_MCAST_MEMBERSHIP_INTVL,
- IFLA_BR_MCAST_QUERIER_INTVL,
- IFLA_BR_MCAST_QUERY_INTVL,
- IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
- IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
- IFLA_BR_NF_CALL_IPTABLES,
- IFLA_BR_NF_CALL_IP6TABLES,
- IFLA_BR_NF_CALL_ARPTABLES,
- IFLA_BR_VLAN_DEFAULT_PVID,
- IFLA_BR_PAD,
- IFLA_BR_VLAN_STATS_ENABLED,
- IFLA_BR_MCAST_STATS_ENABLED,
- IFLA_BR_MCAST_IGMP_VERSION,
- IFLA_BR_MCAST_MLD_VERSION,
- IFLA_BR_VLAN_STATS_PER_PORT,
- IFLA_BR_MULTI_BOOLOPT,
- __IFLA_BR_MAX,
-};
-
-#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
-
-struct ifla_bridge_id {
- __u8 prio[2];
- __u8 addr[6]; /* ETH_ALEN */
-};
-
-enum {
- BRIDGE_MODE_UNSPEC,
- BRIDGE_MODE_HAIRPIN,
-};
-
-enum {
- IFLA_BRPORT_UNSPEC,
- IFLA_BRPORT_STATE, /* Spanning tree state */
- IFLA_BRPORT_PRIORITY, /* " priority */
- IFLA_BRPORT_COST, /* " cost */
- IFLA_BRPORT_MODE, /* mode (hairpin) */
- IFLA_BRPORT_GUARD, /* bpdu guard */
- IFLA_BRPORT_PROTECT, /* root port protection */
- IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */
- IFLA_BRPORT_LEARNING, /* mac learning */
- IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */
- IFLA_BRPORT_PROXYARP, /* proxy ARP */
- IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */
- IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */
- IFLA_BRPORT_ROOT_ID, /* designated root */
- IFLA_BRPORT_BRIDGE_ID, /* designated bridge */
- IFLA_BRPORT_DESIGNATED_PORT,
- IFLA_BRPORT_DESIGNATED_COST,
- IFLA_BRPORT_ID,
- IFLA_BRPORT_NO,
- IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
- IFLA_BRPORT_CONFIG_PENDING,
- IFLA_BRPORT_MESSAGE_AGE_TIMER,
- IFLA_BRPORT_FORWARD_DELAY_TIMER,
- IFLA_BRPORT_HOLD_TIMER,
- IFLA_BRPORT_FLUSH,
- IFLA_BRPORT_MULTICAST_ROUTER,
- IFLA_BRPORT_PAD,
- IFLA_BRPORT_MCAST_FLOOD,
- IFLA_BRPORT_MCAST_TO_UCAST,
- IFLA_BRPORT_VLAN_TUNNEL,
- IFLA_BRPORT_BCAST_FLOOD,
- IFLA_BRPORT_GROUP_FWD_MASK,
- IFLA_BRPORT_NEIGH_SUPPRESS,
- IFLA_BRPORT_ISOLATED,
- IFLA_BRPORT_BACKUP_PORT,
- __IFLA_BRPORT_MAX
-};
-#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
-
-struct ifla_cacheinfo {
- __u32 max_reasm_len;
- __u32 tstamp; /* ipv6InterfaceTable updated timestamp */
- __u32 reachable_time;
- __u32 retrans_time;
-};
-
-enum {
- IFLA_INFO_UNSPEC,
- IFLA_INFO_KIND,
- IFLA_INFO_DATA,
- IFLA_INFO_XSTATS,
- IFLA_INFO_SLAVE_KIND,
- IFLA_INFO_SLAVE_DATA,
- __IFLA_INFO_MAX,
-};
-
-#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1)
-
-/* VLAN section */
-
-enum {
- IFLA_VLAN_UNSPEC,
- IFLA_VLAN_ID,
- IFLA_VLAN_FLAGS,
- IFLA_VLAN_EGRESS_QOS,
- IFLA_VLAN_INGRESS_QOS,
- IFLA_VLAN_PROTOCOL,
- __IFLA_VLAN_MAX,
-};
-
-#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
-
-struct ifla_vlan_flags {
- __u32 flags;
- __u32 mask;
-};
-
-enum {
- IFLA_VLAN_QOS_UNSPEC,
- IFLA_VLAN_QOS_MAPPING,
- __IFLA_VLAN_QOS_MAX
-};
-
-#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1)
-
-struct ifla_vlan_qos_mapping {
- __u32 from;
- __u32 to;
-};
-
-/* MACVLAN section */
-enum {
- IFLA_MACVLAN_UNSPEC,
- IFLA_MACVLAN_MODE,
- IFLA_MACVLAN_FLAGS,
- IFLA_MACVLAN_MACADDR_MODE,
- IFLA_MACVLAN_MACADDR,
- IFLA_MACVLAN_MACADDR_DATA,
- IFLA_MACVLAN_MACADDR_COUNT,
- __IFLA_MACVLAN_MAX,
-};
-
-#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
-
-enum macvlan_mode {
- MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
- MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */
- MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */
- MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
- MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */
-};
-
-enum macvlan_macaddr_mode {
- MACVLAN_MACADDR_ADD,
- MACVLAN_MACADDR_DEL,
- MACVLAN_MACADDR_FLUSH,
- MACVLAN_MACADDR_SET,
-};
-
-#define MACVLAN_FLAG_NOPROMISC 1
-
-/* VRF section */
-enum {
- IFLA_VRF_UNSPEC,
- IFLA_VRF_TABLE,
- __IFLA_VRF_MAX
-};
-
-#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1)
-
-enum {
- IFLA_VRF_PORT_UNSPEC,
- IFLA_VRF_PORT_TABLE,
- __IFLA_VRF_PORT_MAX
-};
-
-#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1)
-
-/* MACSEC section */
-enum {
- IFLA_MACSEC_UNSPEC,
- IFLA_MACSEC_SCI,
- IFLA_MACSEC_PORT,
- IFLA_MACSEC_ICV_LEN,
- IFLA_MACSEC_CIPHER_SUITE,
- IFLA_MACSEC_WINDOW,
- IFLA_MACSEC_ENCODING_SA,
- IFLA_MACSEC_ENCRYPT,
- IFLA_MACSEC_PROTECT,
- IFLA_MACSEC_INC_SCI,
- IFLA_MACSEC_ES,
- IFLA_MACSEC_SCB,
- IFLA_MACSEC_REPLAY_PROTECT,
- IFLA_MACSEC_VALIDATION,
- IFLA_MACSEC_PAD,
- __IFLA_MACSEC_MAX,
-};
-
-#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
-
-/* XFRM section */
-enum {
- IFLA_XFRM_UNSPEC,
- IFLA_XFRM_LINK,
- IFLA_XFRM_IF_ID,
- __IFLA_XFRM_MAX
-};
-
-#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
-
-enum macsec_validation_type {
- MACSEC_VALIDATE_DISABLED = 0,
- MACSEC_VALIDATE_CHECK = 1,
- MACSEC_VALIDATE_STRICT = 2,
- __MACSEC_VALIDATE_END,
- MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1,
-};
-
-/* IPVLAN section */
-enum {
- IFLA_IPVLAN_UNSPEC,
- IFLA_IPVLAN_MODE,
- IFLA_IPVLAN_FLAGS,
- __IFLA_IPVLAN_MAX
-};
-
-#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
-
-enum ipvlan_mode {
- IPVLAN_MODE_L2 = 0,
- IPVLAN_MODE_L3,
- IPVLAN_MODE_L3S,
- IPVLAN_MODE_MAX
-};
-
-#define IPVLAN_F_PRIVATE 0x01
-#define IPVLAN_F_VEPA 0x02
-
-/* VXLAN section */
-enum {
- IFLA_VXLAN_UNSPEC,
- IFLA_VXLAN_ID,
- IFLA_VXLAN_GROUP, /* group or remote address */
- IFLA_VXLAN_LINK,
- IFLA_VXLAN_LOCAL,
- IFLA_VXLAN_TTL,
- IFLA_VXLAN_TOS,
- IFLA_VXLAN_LEARNING,
- IFLA_VXLAN_AGEING,
- IFLA_VXLAN_LIMIT,
- IFLA_VXLAN_PORT_RANGE, /* source port */
- IFLA_VXLAN_PROXY,
- IFLA_VXLAN_RSC,
- IFLA_VXLAN_L2MISS,
- IFLA_VXLAN_L3MISS,
- IFLA_VXLAN_PORT, /* destination port */
- IFLA_VXLAN_GROUP6,
- IFLA_VXLAN_LOCAL6,
- IFLA_VXLAN_UDP_CSUM,
- IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
- IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
- IFLA_VXLAN_REMCSUM_TX,
- IFLA_VXLAN_REMCSUM_RX,
- IFLA_VXLAN_GBP,
- IFLA_VXLAN_REMCSUM_NOPARTIAL,
- IFLA_VXLAN_COLLECT_METADATA,
- IFLA_VXLAN_LABEL,
- IFLA_VXLAN_GPE,
- IFLA_VXLAN_TTL_INHERIT,
- IFLA_VXLAN_DF,
- __IFLA_VXLAN_MAX
-};
-#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
-
-struct ifla_vxlan_port_range {
- __be16 low;
- __be16 high;
-};
-
-enum ifla_vxlan_df {
- VXLAN_DF_UNSET = 0,
- VXLAN_DF_SET,
- VXLAN_DF_INHERIT,
- __VXLAN_DF_END,
- VXLAN_DF_MAX = __VXLAN_DF_END - 1,
-};
-
-/* GENEVE section */
-enum {
- IFLA_GENEVE_UNSPEC,
- IFLA_GENEVE_ID,
- IFLA_GENEVE_REMOTE,
- IFLA_GENEVE_TTL,
- IFLA_GENEVE_TOS,
- IFLA_GENEVE_PORT, /* destination port */
- IFLA_GENEVE_COLLECT_METADATA,
- IFLA_GENEVE_REMOTE6,
- IFLA_GENEVE_UDP_CSUM,
- IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
- IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
- IFLA_GENEVE_LABEL,
- IFLA_GENEVE_TTL_INHERIT,
- IFLA_GENEVE_DF,
- __IFLA_GENEVE_MAX
-};
-#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
-
-enum ifla_geneve_df {
- GENEVE_DF_UNSET = 0,
- GENEVE_DF_SET,
- GENEVE_DF_INHERIT,
- __GENEVE_DF_END,
- GENEVE_DF_MAX = __GENEVE_DF_END - 1,
-};
-
-/* PPP section */
-enum {
- IFLA_PPP_UNSPEC,
- IFLA_PPP_DEV_FD,
- __IFLA_PPP_MAX
-};
-#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
-
-/* GTP section */
-
-enum ifla_gtp_role {
- GTP_ROLE_GGSN = 0,
- GTP_ROLE_SGSN,
-};
-
-enum {
- IFLA_GTP_UNSPEC,
- IFLA_GTP_FD0,
- IFLA_GTP_FD1,
- IFLA_GTP_PDP_HASHSIZE,
- IFLA_GTP_ROLE,
- __IFLA_GTP_MAX,
-};
-#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
-
-/* Bonding section */
-
-enum {
- IFLA_BOND_UNSPEC,
- IFLA_BOND_MODE,
- IFLA_BOND_ACTIVE_SLAVE,
- IFLA_BOND_MIIMON,
- IFLA_BOND_UPDELAY,
- IFLA_BOND_DOWNDELAY,
- IFLA_BOND_USE_CARRIER,
- IFLA_BOND_ARP_INTERVAL,
- IFLA_BOND_ARP_IP_TARGET,
- IFLA_BOND_ARP_VALIDATE,
- IFLA_BOND_ARP_ALL_TARGETS,
- IFLA_BOND_PRIMARY,
- IFLA_BOND_PRIMARY_RESELECT,
- IFLA_BOND_FAIL_OVER_MAC,
- IFLA_BOND_XMIT_HASH_POLICY,
- IFLA_BOND_RESEND_IGMP,
- IFLA_BOND_NUM_PEER_NOTIF,
- IFLA_BOND_ALL_SLAVES_ACTIVE,
- IFLA_BOND_MIN_LINKS,
- IFLA_BOND_LP_INTERVAL,
- IFLA_BOND_PACKETS_PER_SLAVE,
- IFLA_BOND_AD_LACP_RATE,
- IFLA_BOND_AD_SELECT,
- IFLA_BOND_AD_INFO,
- IFLA_BOND_AD_ACTOR_SYS_PRIO,
- IFLA_BOND_AD_USER_PORT_KEY,
- IFLA_BOND_AD_ACTOR_SYSTEM,
- IFLA_BOND_TLB_DYNAMIC_LB,
- __IFLA_BOND_MAX,
-};
-
-#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
-
-enum {
- IFLA_BOND_AD_INFO_UNSPEC,
- IFLA_BOND_AD_INFO_AGGREGATOR,
- IFLA_BOND_AD_INFO_NUM_PORTS,
- IFLA_BOND_AD_INFO_ACTOR_KEY,
- IFLA_BOND_AD_INFO_PARTNER_KEY,
- IFLA_BOND_AD_INFO_PARTNER_MAC,
- __IFLA_BOND_AD_INFO_MAX,
-};
-
-#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1)
-
-enum {
- IFLA_BOND_SLAVE_UNSPEC,
- IFLA_BOND_SLAVE_STATE,
- IFLA_BOND_SLAVE_MII_STATUS,
- IFLA_BOND_SLAVE_LINK_FAILURE_COUNT,
- IFLA_BOND_SLAVE_PERM_HWADDR,
- IFLA_BOND_SLAVE_QUEUE_ID,
- IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
- IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
- IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE,
- __IFLA_BOND_SLAVE_MAX,
-};
-
-#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1)
-
-/* SR-IOV virtual function management section */
-
-enum {
- IFLA_VF_INFO_UNSPEC,
- IFLA_VF_INFO,
- __IFLA_VF_INFO_MAX,
-};
-
-#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
-
-enum {
- IFLA_VF_UNSPEC,
- IFLA_VF_MAC, /* Hardware queue specific attributes */
- IFLA_VF_VLAN, /* VLAN ID and QoS */
- IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */
- IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */
- IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */
- IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */
- IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query
- * on/off switch
- */
- IFLA_VF_STATS, /* network device statistics */
- IFLA_VF_TRUST, /* Trust VF */
- IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */
- IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */
- IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */
- __IFLA_VF_MAX,
-};
-
-#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
-
-struct ifla_vf_mac {
- __u32 vf;
- __u8 mac[32]; /* MAX_ADDR_LEN */
-};
-
-struct ifla_vf_vlan {
- __u32 vf;
- __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
- __u32 qos;
-};
-
-enum {
- IFLA_VF_VLAN_INFO_UNSPEC,
- IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */
- __IFLA_VF_VLAN_INFO_MAX,
-};
-
-#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1)
-#define MAX_VLAN_LIST_LEN 1
-
-struct ifla_vf_vlan_info {
- __u32 vf;
- __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
- __u32 qos;
- __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */
-};
-
-struct ifla_vf_tx_rate {
- __u32 vf;
- __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
-};
-
-struct ifla_vf_rate {
- __u32 vf;
- __u32 min_tx_rate; /* Min Bandwidth in Mbps */
- __u32 max_tx_rate; /* Max Bandwidth in Mbps */
-};
-
-struct ifla_vf_spoofchk {
- __u32 vf;
- __u32 setting;
-};
-
-struct ifla_vf_guid {
- __u32 vf;
- __u64 guid;
-};
-
-enum {
- IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */
- IFLA_VF_LINK_STATE_ENABLE, /* link always up */
- IFLA_VF_LINK_STATE_DISABLE, /* link always down */
- __IFLA_VF_LINK_STATE_MAX,
-};
-
-struct ifla_vf_link_state {
- __u32 vf;
- __u32 link_state;
-};
-
-struct ifla_vf_rss_query_en {
- __u32 vf;
- __u32 setting;
-};
-
-enum {
- IFLA_VF_STATS_RX_PACKETS,
- IFLA_VF_STATS_TX_PACKETS,
- IFLA_VF_STATS_RX_BYTES,
- IFLA_VF_STATS_TX_BYTES,
- IFLA_VF_STATS_BROADCAST,
- IFLA_VF_STATS_MULTICAST,
- IFLA_VF_STATS_PAD,
- IFLA_VF_STATS_RX_DROPPED,
- IFLA_VF_STATS_TX_DROPPED,
- __IFLA_VF_STATS_MAX,
-};
-
-#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1)
-
-struct ifla_vf_trust {
- __u32 vf;
- __u32 setting;
-};
-
-/* VF ports management section
- *
- * Nested layout of set/get msg is:
- *
- * [IFLA_NUM_VF]
- * [IFLA_VF_PORTS]
- * [IFLA_VF_PORT]
- * [IFLA_PORT_*], ...
- * [IFLA_VF_PORT]
- * [IFLA_PORT_*], ...
- * ...
- * [IFLA_PORT_SELF]
- * [IFLA_PORT_*], ...
- */
-
-enum {
- IFLA_VF_PORT_UNSPEC,
- IFLA_VF_PORT, /* nest */
- __IFLA_VF_PORT_MAX,
-};
-
-#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1)
-
-enum {
- IFLA_PORT_UNSPEC,
- IFLA_PORT_VF, /* __u32 */
- IFLA_PORT_PROFILE, /* string */
- IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */
- IFLA_PORT_INSTANCE_UUID, /* binary UUID */
- IFLA_PORT_HOST_UUID, /* binary UUID */
- IFLA_PORT_REQUEST, /* __u8 */
- IFLA_PORT_RESPONSE, /* __u16, output only */
- __IFLA_PORT_MAX,
-};
-
-#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1)
-
-#define PORT_PROFILE_MAX 40
-#define PORT_UUID_MAX 16
-#define PORT_SELF_VF -1
-
-enum {
- PORT_REQUEST_PREASSOCIATE = 0,
- PORT_REQUEST_PREASSOCIATE_RR,
- PORT_REQUEST_ASSOCIATE,
- PORT_REQUEST_DISASSOCIATE,
-};
-
-enum {
- PORT_VDP_RESPONSE_SUCCESS = 0,
- PORT_VDP_RESPONSE_INVALID_FORMAT,
- PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES,
- PORT_VDP_RESPONSE_UNUSED_VTID,
- PORT_VDP_RESPONSE_VTID_VIOLATION,
- PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION,
- PORT_VDP_RESPONSE_OUT_OF_SYNC,
- /* 0x08-0xFF reserved for future VDP use */
- PORT_PROFILE_RESPONSE_SUCCESS = 0x100,
- PORT_PROFILE_RESPONSE_INPROGRESS,
- PORT_PROFILE_RESPONSE_INVALID,
- PORT_PROFILE_RESPONSE_BADSTATE,
- PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES,
- PORT_PROFILE_RESPONSE_ERROR,
-};
-
-struct ifla_port_vsi {
- __u8 vsi_mgr_id;
- __u8 vsi_type_id[3];
- __u8 vsi_type_version;
- __u8 pad[3];
-};
-
-
-/* IPoIB section */
-
-enum {
- IFLA_IPOIB_UNSPEC,
- IFLA_IPOIB_PKEY,
- IFLA_IPOIB_MODE,
- IFLA_IPOIB_UMCAST,
- __IFLA_IPOIB_MAX
-};
-
-enum {
- IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */
- IPOIB_MODE_CONNECTED = 1, /* using connected QPs */
-};
-
-#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
-
-
-/* HSR section */
-
-enum {
- IFLA_HSR_UNSPEC,
- IFLA_HSR_SLAVE1,
- IFLA_HSR_SLAVE2,
- IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */
- IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */
- IFLA_HSR_SEQ_NR,
- IFLA_HSR_VERSION, /* HSR version */
- __IFLA_HSR_MAX,
-};
-
-#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
-
-/* STATS section */
-
-struct if_stats_msg {
- __u8 family;
- __u8 pad1;
- __u16 pad2;
- __u32 ifindex;
- __u32 filter_mask;
-};
-
-/* A stats attribute can be netdev specific or a global stat.
- * For netdev stats, lets use the prefix IFLA_STATS_LINK_*
- */
-enum {
- IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
- IFLA_STATS_LINK_64,
- IFLA_STATS_LINK_XSTATS,
- IFLA_STATS_LINK_XSTATS_SLAVE,
- IFLA_STATS_LINK_OFFLOAD_XSTATS,
- IFLA_STATS_AF_SPEC,
- __IFLA_STATS_MAX,
-};
-
-#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1)
-
-#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1))
-
-/* These are embedded into IFLA_STATS_LINK_XSTATS:
- * [IFLA_STATS_LINK_XSTATS]
- * -> [LINK_XSTATS_TYPE_xxx]
- * -> [rtnl link type specific attributes]
- */
-enum {
- LINK_XSTATS_TYPE_UNSPEC,
- LINK_XSTATS_TYPE_BRIDGE,
- LINK_XSTATS_TYPE_BOND,
- __LINK_XSTATS_TYPE_MAX
-};
-#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
-
-/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */
-enum {
- IFLA_OFFLOAD_XSTATS_UNSPEC,
- IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
- __IFLA_OFFLOAD_XSTATS_MAX
-};
-#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
-
-/* XDP section */
-
-#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0)
-#define XDP_FLAGS_SKB_MODE (1U << 1)
-#define XDP_FLAGS_DRV_MODE (1U << 2)
-#define XDP_FLAGS_HW_MODE (1U << 3)
-#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
- XDP_FLAGS_DRV_MODE | \
- XDP_FLAGS_HW_MODE)
-#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
- XDP_FLAGS_MODES)
-
-/* These are stored into IFLA_XDP_ATTACHED on dump. */
-enum {
- XDP_ATTACHED_NONE = 0,
- XDP_ATTACHED_DRV,
- XDP_ATTACHED_SKB,
- XDP_ATTACHED_HW,
- XDP_ATTACHED_MULTI,
-};
-
-enum {
- IFLA_XDP_UNSPEC,
- IFLA_XDP_FD,
- IFLA_XDP_ATTACHED,
- IFLA_XDP_FLAGS,
- IFLA_XDP_PROG_ID,
- IFLA_XDP_DRV_PROG_ID,
- IFLA_XDP_SKB_PROG_ID,
- IFLA_XDP_HW_PROG_ID,
- __IFLA_XDP_MAX,
-};
-
-#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1)
-
-enum {
- IFLA_EVENT_NONE,
- IFLA_EVENT_REBOOT, /* internal reset / reboot */
- IFLA_EVENT_FEATURES, /* change in offload features */
- IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */
- IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */
- IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */
- IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */
-};
-
-/* tun section */
-
-enum {
- IFLA_TUN_UNSPEC,
- IFLA_TUN_OWNER,
- IFLA_TUN_GROUP,
- IFLA_TUN_TYPE,
- IFLA_TUN_PI,
- IFLA_TUN_VNET_HDR,
- IFLA_TUN_PERSIST,
- IFLA_TUN_MULTI_QUEUE,
- IFLA_TUN_NUM_QUEUES,
- IFLA_TUN_NUM_DISABLED_QUEUES,
- __IFLA_TUN_MAX,
-};
-
-#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1)
-
-/* rmnet section */
-
-#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0)
-#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3)
-
-enum {
- IFLA_RMNET_UNSPEC,
- IFLA_RMNET_MUX_ID,
- IFLA_RMNET_FLAGS,
- __IFLA_RMNET_MAX,
-};
-
-#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1)
-
-struct ifla_rmnet_flags {
- __u32 flags;
- __u32 mask;
-};
-
-#endif /* _UAPI_LINUX_IF_LINK_H */
diff --git a/src/xdp/include/linux/if_xdp.h b/src/xdp/include/linux/if_xdp.h
deleted file mode 100644
index be328c593..000000000
--- a/src/xdp/include/linux/if_xdp.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * if_xdp: XDP socket user-space interface
- * Copyright(c) 2018 Intel Corporation.
- *
- * Author(s): Björn Töpel <bjorn.topel@intel.com>
- * Magnus Karlsson <magnus.karlsson@intel.com>
- */
-
-#ifndef _LINUX_IF_XDP_H
-#define _LINUX_IF_XDP_H
-
-#include <linux/types.h>
-
-/* Options for the sxdp_flags field */
-#define XDP_SHARED_UMEM (1 << 0)
-#define XDP_COPY (1 << 1) /* Force copy-mode */
-#define XDP_ZEROCOPY (1 << 2) /* Force zero-copy mode */
-/* If this option is set, the driver might go sleep and in that case
- * the XDP_RING_NEED_WAKEUP flag in the fill and/or Tx rings will be
- * set. If it is set, the application need to explicitly wake up the
- * driver with a poll() (Rx and Tx) or sendto() (Tx only). If you are
- * running the driver and the application on the same core, you should
- * use this option so that the kernel will yield to the user space
- * application.
- */
-#define XDP_USE_NEED_WAKEUP (1 << 3)
-
-/* Flags for xsk_umem_config flags */
-#define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0)
-
-struct sockaddr_xdp {
- __u16 sxdp_family;
- __u16 sxdp_flags;
- __u32 sxdp_ifindex;
- __u32 sxdp_queue_id;
- __u32 sxdp_shared_umem_fd;
-};
-
-/* XDP_RING flags */
-#define XDP_RING_NEED_WAKEUP (1 << 0)
-
-struct xdp_ring_offset {
- __u64 producer;
- __u64 consumer;
- __u64 desc;
- __u64 flags;
-};
-
-struct xdp_mmap_offsets {
- struct xdp_ring_offset rx;
- struct xdp_ring_offset tx;
- struct xdp_ring_offset fr; /* Fill */
- struct xdp_ring_offset cr; /* Completion */
-};
-
-/* XDP socket options */
-#define XDP_MMAP_OFFSETS 1
-#define XDP_RX_RING 2
-#define XDP_TX_RING 3
-#define XDP_UMEM_REG 4
-#define XDP_UMEM_FILL_RING 5
-#define XDP_UMEM_COMPLETION_RING 6
-#define XDP_STATISTICS 7
-#define XDP_OPTIONS 8
-
-struct xdp_umem_reg {
- __u64 addr; /* Start of packet data area */
- __u64 len; /* Length of packet data area */
- __u32 chunk_size;
- __u32 headroom;
- __u32 flags;
-};
-
-struct xdp_statistics {
- __u64 rx_dropped; /* Dropped for reasons other than invalid desc */
- __u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
- __u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
-};
-
-struct xdp_options {
- __u32 flags;
-};
-
-/* Flags for the flags field of struct xdp_options */
-#define XDP_OPTIONS_ZEROCOPY (1 << 0)
-
-/* Pgoff for mmaping the rings */
-#define XDP_PGOFF_RX_RING 0
-#define XDP_PGOFF_TX_RING 0x80000000
-#define XDP_UMEM_PGOFF_FILL_RING 0x100000000ULL
-#define XDP_UMEM_PGOFF_COMPLETION_RING 0x180000000ULL
-
-/* Masks for unaligned chunks mode */
-#define XSK_UNALIGNED_BUF_OFFSET_SHIFT 48
-#define XSK_UNALIGNED_BUF_ADDR_MASK \
- ((1ULL << XSK_UNALIGNED_BUF_OFFSET_SHIFT) - 1)
-
-/* Rx/Tx descriptor */
-struct xdp_desc {
- __u64 addr;
- __u32 len;
- __u32 options;
-};
-
-/* UMEM descriptor is __u64 */
-
-#endif /* _LINUX_IF_XDP_H */
diff --git a/src/xdp/include/perf-sys.h b/src/xdp/include/perf-sys.h
deleted file mode 100644
index 2fd16e482..000000000
--- a/src/xdp/include/perf-sys.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copied from $(LINUX)/tools/perf/perf-sys.h (kernel 4.18) */
-#ifndef _PERF_SYS_H
-#define _PERF_SYS_H
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
-#include <linux/types.h>
-#include <linux/perf_event.h>
-/*
- * remove the following headers to allow for userspace program compilation
- * #include <linux/compiler.h>
- * #include <asm/barrier.h>
- */
-#ifdef __powerpc__
-#define CPUINFO_PROC {"cpu"}
-#endif
-
-#ifdef __s390__
-#define CPUINFO_PROC {"vendor_id"}
-#endif
-
-#ifdef __sh__
-#define CPUINFO_PROC {"cpu type"}
-#endif
-
-#ifdef __hppa__
-#define CPUINFO_PROC {"cpu"}
-#endif
-
-#ifdef __sparc__
-#define CPUINFO_PROC {"cpu"}
-#endif
-
-#ifdef __alpha__
-#define CPUINFO_PROC {"cpu model"}
-#endif
-
-#ifdef __arm__
-#define CPUINFO_PROC {"model name", "Processor"}
-#endif
-
-#ifdef __mips__
-#define CPUINFO_PROC {"cpu model"}
-#endif
-
-#ifdef __arc__
-#define CPUINFO_PROC {"Processor"}
-#endif
-
-#ifdef __xtensa__
-#define CPUINFO_PROC {"core ID"}
-#endif
-
-#ifndef CPUINFO_PROC
-#define CPUINFO_PROC { "model name", }
-#endif
-
-static inline int
-sys_perf_event_open(struct perf_event_attr *attr,
- pid_t pid, int cpu, int group_fd,
- unsigned long flags)
-{
- int fd;
-
- fd = syscall(__NR_perf_event_open, attr, pid, cpu,
- group_fd, flags);
-
-#ifdef HAVE_ATTR_TEST
- if (unlikely(test_attr__enabled))
- test_attr__open(attr, pid, cpu, fd, group_fd, flags);
-#endif
- return fd;
-}
-
-#endif /* _PERF_SYS_H */
diff --git a/src/xdp/utils/Makefile b/src/xdp/utils/Makefile
deleted file mode 100644
index 42d6e63e3..000000000
--- a/src/xdp/utils/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-
-USER_TARGETS := xdp_stats xdp_loader
-
-LIBBPF_DIR = ../libbpf/src/
-COMMON_DIR = ../common/
-
-# Extend with another COMMON_OBJS
-COMMON_OBJS += $(COMMON_DIR)/common_libbpf.o
-
-include $(COMMON_DIR)/common.mk
diff --git a/src/xdp/utils/xdp_loader.c b/src/xdp/utils/xdp_loader.c
deleted file mode 100644
index d3b05d0c0..000000000
--- a/src/xdp/utils/xdp_loader.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-static const char *__doc__ = "XDP loader\n"
- " - Allows selecting BPF section --progsec name to XDP-attach to --dev\n";
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <locale.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <bpf/bpf.h>
-#include <bpf/libbpf.h>
-
-#include <net/if.h>
-#include <linux/if_link.h> /* depend on kernel-headers installed */
-
-#include "../common/common_params.h"
-#include "../common/common_user_bpf_xdp.h"
-#include "../common/common_libbpf.h"
-
-static const char *default_filename = "xdp_prog_kern.o";
-
-static const struct option_wrapper long_options[] = {
-
- {{"help", no_argument, NULL, 'h' },
- "Show help", false},
-
- {{"dev", required_argument, NULL, 'd' },
- "Operate on device <ifname>", "<ifname>", true},
-
- {{"skb-mode", no_argument, NULL, 'S' },
- "Install XDP program in SKB (AKA generic) mode"},
-
- {{"native-mode", no_argument, NULL, 'N' },
- "Install XDP program in native mode"},
-
- {{"auto-mode", no_argument, NULL, 'A' },
- "Auto-detect SKB or native mode"},
-
- {{"force", no_argument, NULL, 'F' },
- "Force install, replacing existing program on interface"},
-
- {{"unload", no_argument, NULL, 'U' },
- "Unload XDP program instead of loading"},
-
- {{"reuse-maps", no_argument, NULL, 'M' },
- "Reuse pinned maps"},
-
- {{"quiet", no_argument, NULL, 'q' },
- "Quiet mode (no output)"},
-
- {{"filename", required_argument, NULL, 1 },
- "Load program from <file>", "<file>"},
-
- {{"progsec", required_argument, NULL, 2 },
- "Load program in <section> of the ELF file", "<section>"},
-
- {{0, 0, NULL, 0 }, NULL, false}
-};
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-const char *pin_basedir = "/sys/fs/bpf";
-const char *map_name = "xdp_stats_map";
-
-/* Pinning maps under /sys/fs/bpf in subdir */
-int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg)
-{
- char map_filename[PATH_MAX];
- int err, len;
-
- len = snprintf(map_filename, PATH_MAX, "%s/%s/%s",
- pin_basedir, cfg->ifname, map_name);
- if (len < 0) {
- fprintf(stderr, "ERR: creating map_name\n");
- return EXIT_FAIL_OPTION;
- }
-
- /* Existing/previous XDP prog might not have cleaned up */
- if (access(map_filename, F_OK ) != -1 ) {
- if (verbose)
- printf(" - Unpinning (remove) prev maps in %s/\n",
- cfg->pin_dir);
-
- /* Basically calls unlink(3) on map_filename */
- err = bpf_object__unpin_maps(bpf_obj, cfg->pin_dir);
- if (err) {
- fprintf(stderr, "ERR: UNpinning maps in %s\n", cfg->pin_dir);
- return EXIT_FAIL_BPF;
- }
- }
- if (verbose)
- printf(" - Pinning maps in %s/\n", cfg->pin_dir);
-
- /* This will pin all maps in our bpf_object */
- err = bpf_object__pin_maps(bpf_obj, cfg->pin_dir);
- if (err)
- return EXIT_FAIL_BPF;
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- struct bpf_object *bpf_obj;
- int err, len;
-
- struct config cfg = {
- .xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE,
- .ifindex = -1,
- .do_unload = false,
- };
- /* Set default BPF-ELF object file and BPF program name */
- strncpy(cfg.filename, default_filename, sizeof(cfg.filename));
- /* Cmdline options can change progsec */
- parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
-
- /* Required option */
- if (cfg.ifindex == -1) {
- fprintf(stderr, "ERR: required option --dev missing\n\n");
- usage(argv[0], __doc__, long_options, (argc == 1));
- return EXIT_FAIL_OPTION;
- }
- if (cfg.do_unload) {
- if (!cfg.reuse_maps) {
- /* TODO: Miss unpin of maps on unload */
- }
- return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0);
- }
-
- len = snprintf(cfg.pin_dir, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname);
- if (len < 0) {
- fprintf(stderr, "ERR: creating pin dirname\n");
- return EXIT_FAIL_OPTION;
- }
-
-
- bpf_obj = load_bpf_and_xdp_attach(&cfg);
- if (!bpf_obj)
- return EXIT_FAIL_BPF;
-
- if (verbose) {
- printf("Success: Loaded BPF-object(%s) and used section(%s)\n",
- cfg.filename, cfg.progsec);
- printf(" - XDP prog attached on device:%s(ifindex:%d)\n",
- cfg.ifname, cfg.ifindex);
- }
-
- /* Use the --dev name as subdir for exporting/pinning maps */
- if (!cfg.reuse_maps) {
- err = pin_maps_in_bpf_object(bpf_obj, &cfg);
- if (err) {
- fprintf(stderr, "ERR: pinning maps\n");
- return err;
- }
- }
-
- return EXIT_OK;
-}
diff --git a/src/xdp/utils/xdp_stats.c b/src/xdp/utils/xdp_stats.c
deleted file mode 100644
index f9fa7438f..000000000
--- a/src/xdp/utils/xdp_stats.c
+++ /dev/null
@@ -1,298 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-static const char *__doc__ = "XDP stats program\n"
- " - Finding xdp_stats_map via --dev name info\n";
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <locale.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <bpf/bpf.h>
-/* Lesson#1: this prog does not need to #include <bpf/libbpf.h> as it only uses
- * the simple bpf-syscall wrappers, defined in libbpf #include<bpf/bpf.h>
- */
-
-#include <net/if.h>
-#include <linux/if_link.h> /* depend on kernel-headers installed */
-
-#include "../common/common_params.h"
-#include "../common/common_user_bpf_xdp.h"
-#include "../common/xdp_stats_kern_user.h"
-
-#include "../include/bpf_util.h" /* bpf_num_possible_cpus */
-
-static const struct option_wrapper long_options[] = {
- {{"help", no_argument, NULL, 'h' },
- "Show help", false},
-
- {{"dev", required_argument, NULL, 'd' },
- "Operate on device <ifname>", "<ifname>", true},
-
- {{"quiet", no_argument, NULL, 'q' },
- "Quiet mode (no output)"},
-
- {{0, 0, NULL, 0 }}
-};
-
-#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
-static __u64 gettime(void)
-{
- struct timespec t;
- int res;
-
- res = clock_gettime(CLOCK_MONOTONIC, &t);
- if (res < 0) {
- fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
- exit(EXIT_FAIL);
- }
- return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
-}
-
-struct record {
- __u64 timestamp;
- struct datarec total; /* defined in common_kern_user.h */
-};
-
-struct stats_record {
- struct record stats[XDP_ACTION_MAX];
-};
-
-static double calc_period(struct record *r, struct record *p)
-{
- double period_ = 0;
- __u64 period = 0;
-
- period = r->timestamp - p->timestamp;
- if (period > 0)
- period_ = ((double) period / NANOSEC_PER_SEC);
-
- return period_;
-}
-
-static void stats_print_header()
-{
- /* Print stats "header" */
- printf("%-12s\n", "XDP-action");
-}
-
-static void stats_print(struct stats_record *stats_rec,
- struct stats_record *stats_prev)
-{
- struct record *rec, *prev;
- __u64 packets, bytes;
- double period;
- double pps; /* packets per sec */
- double bps; /* bits per sec */
- int i;
-
- stats_print_header(); /* Print stats "header" */
-
- /* Print for each XDP actions stats */
- for (i = 0; i < XDP_ACTION_MAX; i++)
- {
- char *fmt = "%-12s %'11lld pkts (%'10.0f pps)"
- " %'11lld Kbytes (%'6.0f Mbits/s)"
- " period:%f\n";
- const char *action = action2str(i);
-
- rec = &stats_rec->stats[i];
- prev = &stats_prev->stats[i];
-
- period = calc_period(rec, prev);
- if (period == 0)
- return;
-
- packets = rec->total.rx_packets - prev->total.rx_packets;
- pps = packets / period;
-
- bytes = rec->total.rx_bytes - prev->total.rx_bytes;
- bps = (bytes * 8)/ period / 1000000;
-
- printf(fmt, action, rec->total.rx_packets, pps,
- rec->total.rx_bytes / 1000 , bps,
- period);
- }
- printf("\n");
-}
-
-
-/* BPF_MAP_TYPE_ARRAY */
-void map_get_value_array(int fd, __u32 key, struct datarec *value)
-{
- if ((bpf_map_lookup_elem(fd, &key, value)) != 0) {
- fprintf(stderr,
- "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
- }
-}
-
-/* BPF_MAP_TYPE_PERCPU_ARRAY */
-void map_get_value_percpu_array(int fd, __u32 key, struct datarec *value)
-{
- /* For percpu maps, userspace gets a value per possible CPU */
- unsigned int nr_cpus = bpf_num_possible_cpus();
- struct datarec values[nr_cpus];
- __u64 sum_bytes = 0;
- __u64 sum_pkts = 0;
- int i;
-
- if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
- fprintf(stderr,
- "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
- return;
- }
-
- /* Sum values from each CPU */
- for (i = 0; i < nr_cpus; i++) {
- sum_pkts += values[i].rx_packets;
- sum_bytes += values[i].rx_bytes;
- }
- value->rx_packets = sum_pkts;
- value->rx_bytes = sum_bytes;
-}
-
-static bool map_collect(int fd, __u32 map_type, __u32 key, struct record *rec)
-{
- struct datarec value;
-
- /* Get time as close as possible to reading map contents */
- rec->timestamp = gettime();
-
- switch (map_type) {
- case BPF_MAP_TYPE_ARRAY:
- map_get_value_array(fd, key, &value);
- break;
- case BPF_MAP_TYPE_PERCPU_ARRAY:
- map_get_value_percpu_array(fd, key, &value);
- break;
- default:
- fprintf(stderr, "ERR: Unknown map_type(%u) cannot handle\n",
- map_type);
- return false;
- break;
- }
-
- rec->total.rx_packets = value.rx_packets;
- rec->total.rx_bytes = value.rx_bytes;
- return true;
-}
-
-static void stats_collect(int map_fd, __u32 map_type,
- struct stats_record *stats_rec)
-{
- /* Collect all XDP actions stats */
- __u32 key;
-
- for (key = 0; key < XDP_ACTION_MAX; key++) {
- map_collect(map_fd, map_type, key, &stats_rec->stats[key]);
- }
-}
-
-static int stats_poll(const char *pin_dir, int map_fd, __u32 id,
- __u32 map_type, int interval)
-{
- struct bpf_map_info info = {};
- struct stats_record prev, record = { 0 };
- int counter = 0;
-
- /* Trick to pretty printf with thousands separators use %' */
- setlocale(LC_NUMERIC, "en_US");
-
- /* Get initial reading quickly */
- stats_collect(map_fd, map_type, &record);
- usleep(1000000/4);
-
- while (1) {
- prev = record; /* struct copy */
-
- map_fd = open_bpf_map_file(pin_dir, "xdp_stats_map", &info);
- if (map_fd < 0) {
- return EXIT_FAIL_BPF;
- } else if (id != info.id) {
- printf("BPF map xdp_stats_map changed its ID, restarting\n");
- return 0;
- }
-
- stats_collect(map_fd, map_type, &record);
- stats_print(&record, &prev);
- sleep(interval);
- counter++;
- if (counter > 1) {
- return 0;
- }
- }
-
- return 0;
-}
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-const char *pin_basedir = "/sys/fs/bpf";
-
-int main(int argc, char **argv)
-{
- const struct bpf_map_info map_expect = {
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct datarec),
- .max_entries = XDP_ACTION_MAX,
- };
- struct bpf_map_info info = { 0 };
- char pin_dir[PATH_MAX];
- int stats_map_fd;
- int interval = 2;
- int len, err;
-
- struct config cfg = {
- .ifindex = -1,
- .do_unload = false,
- };
-
- /* Cmdline options can change progsec */
- parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
-
- /* Required option */
- if (cfg.ifindex == -1) {
- fprintf(stderr, "ERR: required option --dev missing\n\n");
- usage(argv[0], __doc__, long_options, (argc == 1));
- return EXIT_FAIL_OPTION;
- }
-
- /* Use the --dev name as subdir for finding pinned maps */
- len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname);
- if (len < 0) {
- fprintf(stderr, "ERR: creating pin dirname\n");
- return EXIT_FAIL_OPTION;
- }
-
- stats_map_fd = open_bpf_map_file(pin_dir, "xdp_stats_map", &info);
- if (stats_map_fd < 0) {
- return EXIT_FAIL_BPF;
- }
-
- /* check map info, e.g. datarec is expected size */
- err = check_map_fd_info(&info, &map_expect);
- if (err) {
- fprintf(stderr, "ERR: map via FD not compatible\n");
- return err;
- }
- if (verbose) {
- printf("\nCollecting stats from BPF map\n");
- printf(" - BPF map (bpf_map_type:%d) id:%d name:%s"
- " key_size:%d value_size:%d max_entries:%d\n",
- info.type, info.id, info.name,
- info.key_size, info.value_size, info.max_entries
- );
- }
-
- err = stats_poll(pin_dir, stats_map_fd, info.id, info.type, interval);
- if (err < 0)
- return err;
-
- return EXIT_OK;
-}
diff --git a/src/xdp/xdp_prog_kern.c b/src/xdp/xdp_prog_kern.c
deleted file mode 100644
index 59308325d..000000000
--- a/src/xdp/xdp_prog_kern.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/bpf.h>
-#include <linux/in.h>
-#include <bpf/bpf_helpers.h>
-#include <bpf/bpf_endian.h>
-
-// The parsing helper functions from the packet01 lesson have moved here
-#include "common/parsing_helpers.h"
-#include "common/rewrite_helpers.h"
-
-/* Defines xdp_stats_map */
-#include "common/xdp_stats_kern_user.h"
-#include "common/xdp_stats_kern.h"
-
-#ifndef memcpy
-#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
-#endif
-
-struct {
- __uint(type, BPF_MAP_TYPE_DEVMAP);
- __type(key, int);
- __type(value, int);
- __uint(max_entries, 256);
-} tx_port SEC(".maps");
-
-struct {
- __uint(type, BPF_MAP_TYPE_HASH);
- __type(key, ETH_ALEN);
- __type(value, ETH_ALEN);
- __uint(max_entries, 1);
-} redirect_params SEC(".maps");
-
-static __always_inline __u16 csum_fold_helper(__u32 csum)
-{
- return ~((csum & 0xffff) + (csum >> 16));
-}
-
-/*
- * The icmp_checksum_diff function takes pointers to old and new structures and
- * the old checksum and returns the new checksum. It uses the bpf_csum_diff
- * helper to compute the checksum difference. Note that the sizes passed to the
- * bpf_csum_diff helper should be multiples of 4, as it operates on 32-bit
- * words.
- */
-static __always_inline __u16 icmp_checksum_diff(
- __u16 seed,
- struct icmphdr_common *icmphdr_new,
- struct icmphdr_common *icmphdr_old)
-{
- __u32 csum, size = sizeof(struct icmphdr_common);
-
- csum = bpf_csum_diff((__be32 *)icmphdr_old, size, (__be32 *)icmphdr_new, size, seed);
- return csum_fold_helper(csum);
-}
-
-/* Solution to packet03/assignment-1 */
-SEC("xdp_icmp_echo")
-int xdp_icmp_echo_func(struct xdp_md *ctx)
-{
- void *data_end = (void *)(long)ctx->data_end;
- void *data = (void *)(long)ctx->data;
- struct hdr_cursor nh;
- struct ethhdr *eth;
- int eth_type;
- int ip_type;
- int icmp_type;
- struct iphdr *iphdr;
- struct ipv6hdr *ipv6hdr;
- __u16 echo_reply, old_csum;
- struct icmphdr_common *icmphdr;
- struct icmphdr_common icmphdr_old;
- __u32 action = XDP_PASS;
-
- /* These keep track of the next header type and iterator pointer */
- nh.pos = data;
-
- /* Parse Ethernet and IP/IPv6 headers */
- eth_type = parse_ethhdr(&nh, data_end, &eth);
- if (eth_type == bpf_htons(ETH_P_IP)) {
- ip_type = parse_iphdr(&nh, data_end, &iphdr);
- if (ip_type != IPPROTO_ICMP)
- goto out;
- } else if (eth_type == bpf_htons(ETH_P_IPV6)) {
- ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
- if (ip_type != IPPROTO_ICMPV6)
- goto out;
- } else {
- goto out;
- }
-
- /*
- * We are using a special parser here which returns a stucture
- * containing the "protocol-independent" part of an ICMP or ICMPv6
- * header. For purposes of this Assignment we are not interested in
- * the rest of the structure.
- */
- icmp_type = parse_icmphdr_common(&nh, data_end, &icmphdr);
- if (eth_type == bpf_htons(ETH_P_IP) && icmp_type == ICMP_ECHO) {
- /* Swap IP source and destination */
- swap_src_dst_ipv4(iphdr);
- echo_reply = ICMP_ECHOREPLY;
- } else if (eth_type == bpf_htons(ETH_P_IPV6)
- && icmp_type == ICMPV6_ECHO_REQUEST) {
- /* Swap IPv6 source and destination */
- swap_src_dst_ipv6(ipv6hdr);
- echo_reply = ICMPV6_ECHO_REPLY;
- } else {
- goto out;
- }
-
- /* Swap Ethernet source and destination */
- swap_src_dst_mac(eth);
-
-
- /* Patch the packet and update the checksum.*/
- old_csum = icmphdr->cksum;
- icmphdr->cksum = 0;
- icmphdr_old = *icmphdr;
- icmphdr->type = echo_reply;
- icmphdr->cksum = icmp_checksum_diff(~old_csum, icmphdr, &icmphdr_old);
-
- /* Another, less generic, but a bit more efficient way to update the
- * checksum is listed below. As only one 16-bit word changed, the sum
- * can be patched using this formula: sum' = ~(~sum + ~m0 + m1), where
- * sum' is a new sum, sum is an old sum, m0 and m1 are the old and new
- * 16-bit words, correspondingly. In the formula above the + operation
- * is defined as the following function:
- *
- * static __always_inline __u16 csum16_add(__u16 csum, __u16 addend)
- * {
- * csum += addend;
- * return csum + (csum < addend);
- * }
- *
- * So an alternative code to update the checksum might look like this:
- *
- * __u16 m0 = * (__u16 *) icmphdr;
- * icmphdr->type = echo_reply;
- * __u16 m1 = * (__u16 *) icmphdr;
- * icmphdr->checksum = ~(csum16_add(csum16_add(~icmphdr->checksum, ~m0), m1));
- */
-
- action = XDP_TX;
-
-out:
- return xdp_stats_record_action(ctx, action);
-}
-
-/* Solution to packet03/assignment-2 */
-SEC("xdp_redirect")
-int xdp_redirect_func(struct xdp_md *ctx)
-{
- void *data_end = (void *)(long)ctx->data_end;
- void *data = (void *)(long)ctx->data;
- struct hdr_cursor nh;
- struct ethhdr *eth;
- int eth_type;
- int action = XDP_PASS;
- unsigned char dst[ETH_ALEN] = { /* TODO: put your values here */ };
- unsigned ifindex = 0/* TODO: put your values here */;
-
- /* These keep track of the next header type and iterator pointer */
- nh.pos = data;
-
- /* Parse Ethernet and IP/IPv6 headers */
- eth_type = parse_ethhdr(&nh, data_end, &eth);
- if (eth_type == -1)
- goto out;
-
- /* Set a proper destination address */
- memcpy(eth->h_dest, dst, ETH_ALEN);
- action = bpf_redirect(ifindex, 0);
-
-out:
- return xdp_stats_record_action(ctx, action);
-}
-
-/* Solution to packet03/assignment-3 */
-SEC("xdp_redirect_map")
-int xdp_redirect_map_func(struct xdp_md *ctx)
-{
- void *data_end = (void *)(long)ctx->data_end;
- void *data = (void *)(long)ctx->data;
- struct hdr_cursor nh;
- struct ethhdr *eth;
- int eth_type;
- int action = XDP_PASS;
- unsigned char *dst;
-
- /* These keep track of the next header type and iterator pointer */
- nh.pos = data;
-
- /* Parse Ethernet and IP/IPv6 headers */
- eth_type = parse_ethhdr(&nh, data_end, &eth);
- if (eth_type == -1)
- goto out;
-
- /* Do we know where to redirect this packet? */
- dst = bpf_map_lookup_elem(&redirect_params, eth->h_source);
- if (!dst)
- goto out;
-
- /* Set a proper destination address */
- memcpy(eth->h_dest, dst, ETH_ALEN);
- action = bpf_redirect_map(&tx_port, 0, 0);
-
-out:
- return xdp_stats_record_action(ctx, action);
-}
-
-#ifndef AF_INET
-#define AF_INET 2
-#endif
-#ifndef AF_INET6
-#define AF_INET6 10
-#endif
-#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF)
-
-/* from include/net/ip.h */
-static __always_inline int ip_decrease_ttl(struct iphdr *iph)
-{
- __u32 check = iph->check;
- check += bpf_htons(0x0100);
- iph->check = (__u16)(check + (check >= 0xFFFF));
- return --iph->ttl;
-}
-
-/* Solution to packet03/assignment-4 */
-/* xdp_router is the name of the xdp program */
-SEC("xdp_router")
-int xdp_router_func(struct xdp_md *ctx)
-{
- /* this is the packet context*/
- void *data_end = (void *)(long)ctx->data_end;
- void *data = (void *)(long)ctx->data;
- struct bpf_fib_lookup fib_params = {};
- struct ethhdr *eth = data;
- struct ipv6hdr *ip6h;
- struct iphdr *iph;
- __u16 h_proto;
- __u64 nh_off;
- int rc;
- /* default action is to pass */
- int action = XDP_PASS;
-
- nh_off = sizeof(*eth);
- if (data + nh_off > data_end) {
- action = XDP_DROP;
- goto out;
- }
-
- /* determine if this is IP4 or IPv6 by looking at the Ethernet protocol field */
- h_proto = eth->h_proto;
- if (h_proto == bpf_htons(ETH_P_IP)) {
- /* IPv4 part of the code */
- iph = data + nh_off;
-
- if (iph + 1 > data_end) {
- action = XDP_DROP;
- goto out;
- }
- /* as a real router, we need to check the TTL to prevent never ending loops*/
- if (iph->ttl <= 1)
- goto out;
-
- /* populate the fib_params fields to prepare for the lookup */
- fib_params.family = AF_INET;
- fib_params.tos = iph->tos;
- fib_params.l4_protocol = iph->protocol;
- fib_params.sport = 0;
- fib_params.dport = 0;
- fib_params.tot_len = bpf_ntohs(iph->tot_len);
- fib_params.ipv4_src = iph->saddr;
- fib_params.ipv4_dst = iph->daddr;
- } else if (h_proto == bpf_htons(ETH_P_IPV6)) {
- /* IPv6 part of the code */
- struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src;
- struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst;
-
- ip6h = data + nh_off;
- if (ip6h + 1 > data_end) {
- action = XDP_DROP;
- goto out;
- }
- /* as a real router, we need to check the TTL to prevent never ending loops*/
- if (ip6h->hop_limit <= 1)
- goto out;
-
- /* populate the fib_params fields to prepare for the lookup */
- fib_params.family = AF_INET6;
- fib_params.flowinfo = *(__be32 *) ip6h & IPV6_FLOWINFO_MASK;
- fib_params.l4_protocol = ip6h->nexthdr;
- fib_params.sport = 0;
- fib_params.dport = 0;
- fib_params.tot_len = bpf_ntohs(ip6h->payload_len);
- *src = ip6h->saddr;
- *dst = ip6h->daddr;
- } else {
- goto out;
- }
-
- fib_params.ifindex = ctx->ingress_ifindex;
-
- /* this is where the FIB lookup happens. If the lookup is successful */
- /* it will populate the fib_params.ifindex with the egress interface index */
-
- rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), 0);
- switch (rc) {
- case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */
- /* we are a router, so we need to decrease the ttl */
- if (h_proto == bpf_htons(ETH_P_IP))
- ip_decrease_ttl(iph);
- else if (h_proto == bpf_htons(ETH_P_IPV6))
- ip6h->hop_limit--;
- /* set the correct new source and destionation mac addresses */
- /* can be found in fib_params.dmac and fib_params.smac */
- memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
- memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
- /* and done, now we set the action to bpf_redirect_map with fib_params.ifindex which is the egress port as paramater */
- action = bpf_redirect_map(&tx_port, fib_params.ifindex, 0);
- break;
- case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */
- case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */
- case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */
- action = XDP_DROP;
- break;
- case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */
- case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */
- case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */
- case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */
- case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */
- /* PASS */
- break;
- }
-
-out:
- /* and done, update stats and return action */
- return xdp_stats_record_action(ctx, action);
-}
-
-SEC("xdp_pass")
-int xdp_pass_func(struct xdp_md *ctx)
-{
- return xdp_stats_record_action(ctx, XDP_PASS);
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/src/xdp/xdp_prog_user.c b/src/xdp/xdp_prog_user.c
deleted file mode 100644
index c47d2977d..000000000
--- a/src/xdp/xdp_prog_user.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-static const char *__doc__ = "XDP redirect helper\n"
- " - Allows to populate/query tx_port and redirect_params maps\n";
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-#include <stdbool.h>
-
-#include <locale.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <bpf/bpf.h>
-#include <bpf/libbpf.h>
-
-#include <net/if.h>
-#include <linux/if_ether.h>
-#include <linux/if_link.h> /* depend on kernel-headers installed */
-
-#include "common/common_params.h"
-#include "common/common_user_bpf_xdp.h"
-#include "common/common_libbpf.h"
-
-#include "common/xdp_stats_kern_user.h"
-
-static const struct option_wrapper long_options[] = {
-
- {{"help", no_argument, NULL, 'h' },
- "Show help", false},
-
- {{"dev", required_argument, NULL, 'd' },
- "Operate on device <ifname>", "<ifname>", true},
-
- {{"redirect-dev", required_argument, NULL, 'r' },
- "Redirect to device <ifname>", "<ifname>", true},
-
- {{"src-mac", required_argument, NULL, 'L' },
- "Source MAC address of <dev>", "<mac>", true },
-
- {{"dest-mac", required_argument, NULL, 'R' },
- "Destination MAC address of <redirect-dev>", "<mac>", true },
-
- {{"quiet", no_argument, NULL, 'q' },
- "Quiet mode (no output)"},
-
- {{0, 0, NULL, 0 }, NULL, false}
-};
-
-static int parse_u8(char *str, unsigned char *x)
-{
- unsigned long z;
-
- z = strtoul(str, 0, 16);
- if (z > 0xff)
- return -1;
-
- if (x)
- *x = z;
-
- return 0;
-}
-
-static int parse_mac(char *str, unsigned char mac[ETH_ALEN])
-{
- if (parse_u8(str, &mac[0]) < 0)
- return -1;
- if (parse_u8(str + 3, &mac[1]) < 0)
- return -1;
- if (parse_u8(str + 6, &mac[2]) < 0)
- return -1;
- if (parse_u8(str + 9, &mac[3]) < 0)
- return -1;
- if (parse_u8(str + 12, &mac[4]) < 0)
- return -1;
- if (parse_u8(str + 15, &mac[5]) < 0)
- return -1;
-
- return 0;
-}
-
-static int write_iface_params(int map_fd, unsigned char *src, unsigned char *dest)
-{
- if (bpf_map_update_elem(map_fd, src, dest, 0) < 0) {
- fprintf(stderr,
- "WARN: Failed to update bpf map file: err(%d):%s\n",
- errno, strerror(errno));
- return -1;
- }
-
- printf("forward: %02x:%02x:%02x:%02x:%02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x\n",
- src[0], src[1], src[2], src[3], src[4], src[5],
- dest[0], dest[1], dest[2], dest[3], dest[4], dest[5]
- );
-
- return 0;
-}
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-const char *pin_basedir = "/sys/fs/bpf";
-
-int main(int argc, char **argv)
-{
- int i;
- int len;
- int map_fd;
- bool redirect_map;
- char pin_dir[PATH_MAX];
- unsigned char src[ETH_ALEN];
- unsigned char dest[ETH_ALEN];
-
- struct config cfg = {
- .ifindex = -1,
- .redirect_ifindex = -1,
- };
-
- /* Cmdline options can change progsec */
- parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
-
- redirect_map = (cfg.ifindex > 0) && (cfg.redirect_ifindex > 0);
-
- if (cfg.redirect_ifindex > 0 && cfg.ifindex == -1) {
- fprintf(stderr, "ERR: required option --dev missing\n\n");
- usage(argv[0], __doc__, long_options, (argc == 1));
- return EXIT_FAIL_OPTION;
- }
-
- len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname);
- if (len < 0) {
- fprintf(stderr, "ERR: creating pin dirname\n");
- return EXIT_FAIL_OPTION;
- }
-
- if (parse_mac(cfg.src_mac, src) < 0) {
- fprintf(stderr, "ERR: can't parse mac address %s\n", cfg.src_mac);
- return EXIT_FAIL_OPTION;
- }
-
- if (parse_mac(cfg.dest_mac, dest) < 0) {
- fprintf(stderr, "ERR: can't parse mac address %s\n", cfg.dest_mac);
- return EXIT_FAIL_OPTION;
- }
-
- /* Open the tx_port map corresponding to the cfg.ifname interface */
- map_fd = open_bpf_map_file(pin_dir, "tx_port", NULL);
- if (map_fd < 0) {
- return EXIT_FAIL_BPF;
- }
-
- printf("map dir: %s\n", pin_dir);
-
- if (redirect_map) {
- /* setup a virtual port for the static redirect */
- i = 0;
- bpf_map_update_elem(map_fd, &i, &cfg.redirect_ifindex, 0);
- printf("redirect from ifnum=%d to ifnum=%d\n", cfg.ifindex, cfg.redirect_ifindex);
-
- /* Open the redirect_params map */
- map_fd = open_bpf_map_file(pin_dir, "redirect_params", NULL);
- if (map_fd < 0) {
- return EXIT_FAIL_BPF;
- }
-
- /* Setup the mapping containing MAC addresses */
- if (write_iface_params(map_fd, src, dest) < 0) {
- fprintf(stderr, "can't write iface params\n");
- return 1;
- }
- } else {
- /* setup 1-1 mapping for the dynamic router */
- for (i = 1; i < 256; ++i)
- bpf_map_update_elem(map_fd, &i, &i, 0);
- }
-
- return EXIT_OK;
-}